【问题标题】:Scala: find all chains in a listScala:查找列表中的所有链
【发布时间】:2015-11-05 08:07:46
【问题描述】:

假设我有一个项目列表:

Seq(A, B, B, B, B, G, G, S, S, S, B, A, G)

我想找到所有的链并得到它们的序列,如下所示:

Seq(Seq(A), Seq(B, B, B, B), Seq(G, G), Seq(S, S, S), Seq(B), Seq(A), Seq(G))

我想保持顺序,并使用自定义比较函数来确定两个对象是否“相同”。我在想折叠或扫描可能是我需要的,但我在想出确切的情况时遇到了麻烦。我正在使用 Scala。

编辑:我已经修改了类似问题的答案以得到这个:

def collapse(input: Seq[Stmt]): Seq[Seq[Stmt]] = {
    val (l, r) = input.span(_.getClass == input.head.getClass)
    l :: collapse(r)
}

【问题讨论】:

标签: list scala functional-programming sequence fold


【解决方案1】:

更清洁的解决方案:

  def pack[T](input: List[T]): List[List[T]] =
    input.foldRight(Nil : List[List[T]]) ((e, accu) => accu match {
      case Nil => List(List(e))
      case curList@(h :: t) if e == h => List(e) :: curList
      case curList@(h :: t) => List(List(e)) ::: curList
  })

不使用任何库函数(丑陋):

  def pack[T](input: List[T]): List[List[T]] = {
    def packWithPrevious(remaining: List[T])(previous: List[T]): List[List[T]] =
      remaining match {
        case List() => List(previous)
        case head :: tail =>
          val nextIter = packWithPrevious(tail)(_)
          previous match {
            case List() => nextIter(List(head))
            case prevHead :: _ =>
              if (head != prevHead)
                previous :: nextIter(List(head))
              else
                nextIter(head :: previous)
          }
      }
    packWithPrevious(input)(List())
  }

scala> val s = List('A', 'B', 'B', 'B', 'B', 'G', 'G', 'S', 'S', 'S', 'B', 'A', 'G')
s: List[Char] = List(A, B, B, B, B, G, G, S, S, S, B, A, G)

scala> pack(s)
res2: List[List[Char]] = List(List(A), List(B, B, B, B), List(G, G), List(S, S, S), List(B), List(A), List(G))

来源:https://github.com/izmailoff/scala-s-99/blob/master/src/main/scala/s99/p09/P09.scala

测试:https://github.com/izmailoff/scala-s-99/blob/master/src/test/scala/s99/p09/P09Suite.scala

【讨论】:

    【解决方案2】:

    与现有答案类似,但我发现直接在 foldLeft 中使用部分函数作为一个干净的解决方案:

    val s = Seq("A", "B", "B", "B", "B", "G", "G", "S", "S", "S", "B", "A", "G")
    
    s.foldLeft(Seq[Seq[String]]()) {
      case (Seq(), item) => Seq(Seq(item))
      case (head::tail, item) if head.contains(item) => (item +: head) +: tail
      case (seq, item) => Seq(item) +: seq
    }.reverse
    
    res0: Seq[Seq[String]] = List(List(A), List(B, B, B, B), List(G, G), List(S, S, S), List(B), List(A), List(G))
    

    【讨论】:

      【解决方案3】:

      考虑以下解决方案:

      seq.foldLeft(List(List(seq.head))) { case (acc,item)=> 
        if(acc.head.head==item) (item::acc.head)::acc.tail else List(item)::acc  
      }.reverse
      

      seq 可能为空,所以:

      seq.foldLeft(List(seq.headOption.toList)) { case (acc,item)=> 
        if(acc.head.head==item) (item::acc.head)::acc.tail else List(item)::acc  
      }.reverse
      

      【讨论】:

      • 如果seq 为空会怎样?
      • 每个子列表都是向后的,但我可以解决这个问题。谢谢。另外我会添加一个空列表检查。
      【解决方案4】:

      我认为groupBy 在这里会有所帮助,但我的解决方案有点尴尬:

      val seq = Seq("A", "B", "B", "B", "B", "G", "G", "S", "S", "S", "B", "A", "G")
      val parts = {
        var lastKey: Option[(Int, String)] = None
        seq.groupBy(s => {
          lastKey = lastKey.map((p: (Int, String)) =>
            if (p._2.equalsIgnoreCase(s)) p else (p._1 + 1, s)) orElse Some((0, s))
          lastKey.get
        }).toSeq.sortBy(q => q._1).flatMap(q => q._2)
      }
      

      (使用equalsIgnoreCase作为比较函数的例子)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-05-21
        • 1970-01-01
        • 1970-01-01
        • 2013-11-21
        • 2017-08-10
        • 2021-06-11
        • 1970-01-01
        • 2017-04-27
        相关资源
        最近更新 更多