如何扩展Scala列表以不按显式位置而是按给定谓词/条件进行切片

elm*_*elm 2 collections scala list slice

对于

trait Item
case class TypeA(i: Int) extends Item
case class TypeB(i: Int) extends Item
Run Code Online (Sandbox Code Playgroud)

考虑一个Scala项目列表,例如

val myList = List(TypeA(1), TypeB(11), TypeB(12), 
                  TypeA(2), TypeB(21), 
                  TypeA(3), TypeB(31))
Run Code Online (Sandbox Code Playgroud)

目的是定义一个新slice方法,该方法可以应用到myList谓词或条件作为参数。例如

myList.slice { x => x.isInstanceOf[TypeA] }
Run Code Online (Sandbox Code Playgroud)

会提供

List(List(TypeA(1), TypeB(11), TypeB(12)), 
     List(TypeA(2), TypeB(21)), 
     List(TypeA(3), TypeB(31)))
Run Code Online (Sandbox Code Playgroud)

在此示例中,将通过以下方式获得相同的结果

myList.slice { case TypeA(x) => x < 10 }
Run Code Online (Sandbox Code Playgroud)

非常感谢。

Kev*_*ght 5

List已经有一个slice方法-它需要在开始索引和结束索引之间包含元素的子集。您正在寻找的是该span方法的重复应用:

def span(p: (A) ? Boolean): (List[A], List[A])
Run Code Online (Sandbox Code Playgroud)

记录为:

根据谓词将此列表拆分为前缀/后缀对。

注意:如果对谓词p的求值不会引起任何副作用,则c span p等效于(但可能比p更有效)。

返回:一对,该列表的所有元素都满足p的列表的最长前缀,以及此列表的其余部分。

通过重复使用带有反谓词的此方法和附加的逻辑以确保没有一个返回的列表为空,可以得到所需的内容。

import annotation.tailrec

def multiSpan[A](xs: List[A])(splitOn: (A) => Boolean): List[List[A]] = {
  @tailrec
  def loop(xs: List[A], acc: List[List[A]]) : List[List[A]] = xs match {
    case Nil => acc

    case x :: Nil => List(x) :: acc

    case h :: t =>
      val (pre,post) = t.span(!splitOn(_))
      loop(post, (h :: pre) :: acc)
  }
  loop(xs, Nil).reverse
}
Run Code Online (Sandbox Code Playgroud)

更新

根据原始帖子的评论要求,这是一个丰富列表而不是独立方法的版本:

implicit class AddMultispanToList[A](val list: List[A]) extends AnyVal {
  def multiSpan(splitOn: (A) => Boolean): List[List[A]] = {
    @tailrec
    def loop(xs: List[A], acc: List[List[A]]) : List[List[A]] = xs match {
      case Nil => acc

      case x :: Nil => List(x) :: acc

      case h :: t =>
        val (pre,post) = t.span(!splitOn(_))
        loop(post, (h :: pre) :: acc)
    }
    loop(list, Nil).reverse
  }
}
Run Code Online (Sandbox Code Playgroud)

用于:

myList.multiSpan(_.isInstanceOf[TypeA])
Run Code Online (Sandbox Code Playgroud)