【问题标题】:Collapse subsequence of matching elements in a Scala sequence折叠 Scala 序列中匹配元素的子序列
【发布时间】:2015-02-19 17:54:03
【问题描述】:

给定一个 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
}

有没有一种优雅(/实用)的方式来做到这一点?

【问题讨论】:

    标签: scala collections


    【解决方案1】:

    使用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这些嵌套列表通过考虑三种情况:条件是否不成立,因此我们应用一个函数;是否为单例列表(条件成立);否则第一个元素是否成立,其余的是否需要应用函数。

    【讨论】:

      【解决方案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)
      

      【讨论】:

      • 这让我很高兴List(2, 1, 0, 1, 3, 2, 2, 1, 1)。我认为“所有子序列”比我需要的更具组合性。
      • 我完全误解了你的问题。我现在明白了。给我一分钟。
      【解决方案3】:

      这是我对递归实现的尝试

      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)
      

      【讨论】:

        猜你喜欢
        • 2015-05-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-05-23
        • 2013-08-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多