折叠Scala序列中匹配元素的子序列

时间:2015-02-19 00:21:43

标签: scala collections

给出Scala序列......

val sequence: Seq = List( 3, 1, 4, 1, 5, 9, 2, 6, 5 )

...说我想找到符合某些标准的所有子序列,例如奇数的字符串,并用该子序列上的某些操作的结果替换那些字符串,例如它的长度,产生一个新的序列:

val sequence2: Seq = List( 2, 4, 3, 2, 6, 1 )

(是的,这是一个相当人为的例子,但它很简洁。)

到目前为止,我能做的最好的是这个丑陋的命令式黑客攻击:

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
}

有一种优雅(/功能)的方法吗?

3 个答案:

答案 0 :(得分:3)

使用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这些嵌套列表:条件是否不成立,因此我们应用了一个函数;是否是单身人士名单(条件成立);或者第一个元素是否成立,其余部分是否需要应用函数。

答案 1 :(得分:2)

您要找的是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

您可以详细了解foldfoldLeftfoldRight以及其他有用的功能herehere

我原本以为你要求序列的所有后续序列。这可能在类似的情况下有用,所以我会留在这里。您可以同时使用initstails获取所有子序列,然后将filtermap用于您的目的:

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)

答案 2 :(得分:1)

这是我尝试递归实现

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)