折叠 Scala 序列中匹配元素的子序列
Collapse subsequence of matching elements in a Scala sequence
给定一个 Scala 序列...
val sequence: Seq = List( 3, 1, 4, 1, 5, 9, 2, 6, 5 )
...说我想找到符合特定条件的所有子序列,例如奇数串,并用对该子序列的某些操作的结果替换它们,比如它的长度,产生一个新序列:
val sequence2: Seq = List( 2, 4, 3, 2, 6, 1 )
(是的,这是一个相当人为的例子,但它很简洁。)
到目前为止,我能做的最好的事情就是这个丑陋的命令式 hack:
val sequence: Seq[Int] = List( 3, 1, 4, 1, 5, 9, 2, 6, 5 )
var sequence2 = List[Int]() // this is going to be our result
var subsequence = List[Int]()
for (s <- sequence) {
if (s % 2 == 0) {
if (!subsequence.isEmpty) {
sequence2 = sequence2 :+ subsequence.length
subsequence = List[Int]()
}
sequence2 = sequence2 :+ s
} else {
subsequence = subsequence :+ s
}
}
if (!subsequence.isEmpty) {
sequence2 = sequence2 :+ subsequence.length
}
是否有一种优雅的(/实用的)方式来做到这一点?
您要找的是 fold
:
sequence.foldLeft(List(0)) { (soFar, next) =>
if(next % 2 == 0) soFar :+ next :+ 0 else soFar.init :+ (soFar.last + 1)
}.filter(_ != 0)
或换一种风格:
(List(0) /: sequence) {
case(soFar, next) if next % 2 == 0 => soFar :+ next :+ 0
case(soFar, _) => soFar.init :+ (soFar.last + 1)
}.filter(_ != 0)
或者使用 foldRight
代替,有时性能更高:
(sequence :\ List(0)) {
case(next, soFar) if next % 2 == 0 => 0 :: next :: soFar
case(_, hd::tl) => (hd + 1)::tl
}.filter(_ != 0).reverse
您可以阅读有关 fold
、foldLeft
、foldRight
和其他有用功能的更多信息 here and here。
我原本以为你要的是一个序列的所有子序列。这在类似情况下可能会有用,所以我会把它留在这里。您可以一起使用 inits
和 tails
来获取所有子序列,然后根据您的目的使用 filter
和 map
:
val sequence = List( 3, 1, 4, 1, 5, 9, 2, 6, 5 )
val subsequences = sequence.tails.flatMap(_.inits).toList.distinct
subsequences.filter(_.forall(_ % 2 == 1)).map(_.length)
这是我对递归实现的尝试
def subSequenceApply(list: List[Int], predicate: (Int)=> Boolean, func: (List[Int]) => Int):List[Int] = list match {
case Nil => Nil
case h :: t if !predicate(h) => h :: subSequenceApply(t, predicate, func)
case _ =>
val (matchSeq,nonMatch) = list.span(predicate)
func(matchSeq) :: subSequenceApply(nonMatch, predicate, func)
}
因此在您的示例中给出了顺序。你可以运行它作为
subSequenceApply(sequence, _ % 2 != 0, _.length)
使用 multiSpan 按给定标准将列表分成子列表,请考虑针对上述问题的此解决方案,
sequence.multiSpan( _ % 2 == 0 ).flatMap {
case h :: xs if h % 2 != 0 => List( (h::xs).length)
case h :: Nil => List(h)
case h :: xs => List(h, xs.length) }
注意
sequence.multiSpan( _ % 2 == 0 )
List(List(3, 1), List(4, 1, 5, 9), List(2), List(6, 5))
因此我们 flatMap
这些嵌套列表考虑了三种情况:条件是否不成立,因此我们应用了一个函数;是否为单例列表(条件成立);否则第一个元素是否成立,其余元素是否需要应用函数。
给定一个 Scala 序列...
val sequence: Seq = List( 3, 1, 4, 1, 5, 9, 2, 6, 5 )
...说我想找到符合特定条件的所有子序列,例如奇数串,并用对该子序列的某些操作的结果替换它们,比如它的长度,产生一个新序列:
val sequence2: Seq = List( 2, 4, 3, 2, 6, 1 )
(是的,这是一个相当人为的例子,但它很简洁。)
到目前为止,我能做的最好的事情就是这个丑陋的命令式 hack:
val sequence: Seq[Int] = List( 3, 1, 4, 1, 5, 9, 2, 6, 5 )
var sequence2 = List[Int]() // this is going to be our result
var subsequence = List[Int]()
for (s <- sequence) {
if (s % 2 == 0) {
if (!subsequence.isEmpty) {
sequence2 = sequence2 :+ subsequence.length
subsequence = List[Int]()
}
sequence2 = sequence2 :+ s
} else {
subsequence = subsequence :+ s
}
}
if (!subsequence.isEmpty) {
sequence2 = sequence2 :+ subsequence.length
}
是否有一种优雅的(/实用的)方式来做到这一点?
您要找的是 fold
:
sequence.foldLeft(List(0)) { (soFar, next) =>
if(next % 2 == 0) soFar :+ next :+ 0 else soFar.init :+ (soFar.last + 1)
}.filter(_ != 0)
或换一种风格:
(List(0) /: sequence) {
case(soFar, next) if next % 2 == 0 => soFar :+ next :+ 0
case(soFar, _) => soFar.init :+ (soFar.last + 1)
}.filter(_ != 0)
或者使用 foldRight
代替,有时性能更高:
(sequence :\ List(0)) {
case(next, soFar) if next % 2 == 0 => 0 :: next :: soFar
case(_, hd::tl) => (hd + 1)::tl
}.filter(_ != 0).reverse
您可以阅读有关 fold
、foldLeft
、foldRight
和其他有用功能的更多信息 here and here。
我原本以为你要的是一个序列的所有子序列。这在类似情况下可能会有用,所以我会把它留在这里。您可以一起使用 inits
和 tails
来获取所有子序列,然后根据您的目的使用 filter
和 map
:
val sequence = List( 3, 1, 4, 1, 5, 9, 2, 6, 5 )
val subsequences = sequence.tails.flatMap(_.inits).toList.distinct
subsequences.filter(_.forall(_ % 2 == 1)).map(_.length)
这是我对递归实现的尝试
def subSequenceApply(list: List[Int], predicate: (Int)=> Boolean, func: (List[Int]) => Int):List[Int] = list match {
case Nil => Nil
case h :: t if !predicate(h) => h :: subSequenceApply(t, predicate, func)
case _ =>
val (matchSeq,nonMatch) = list.span(predicate)
func(matchSeq) :: subSequenceApply(nonMatch, predicate, func)
}
因此在您的示例中给出了顺序。你可以运行它作为
subSequenceApply(sequence, _ % 2 != 0, _.length)
使用 multiSpan 按给定标准将列表分成子列表,请考虑针对上述问题的此解决方案,
sequence.multiSpan( _ % 2 == 0 ).flatMap {
case h :: xs if h % 2 != 0 => List( (h::xs).length)
case h :: Nil => List(h)
case h :: xs => List(h, xs.length) }
注意
sequence.multiSpan( _ % 2 == 0 )
List(List(3, 1), List(4, 1, 5, 9), List(2), List(6, 5))
因此我们 flatMap
这些嵌套列表考虑了三种情况:条件是否不成立,因此我们应用了一个函数;是否为单例列表(条件成立);否则第一个元素是否成立,其余元素是否需要应用函数。