如何扩展一个Scala列表来启用切片,而不是通过显式的位置,而是通过给定的谓词/条件

对于

trait Item case class TypeA(i: Int) extends Item case class TypeB(i: Int) extends Item 

考虑一个斯卡拉项目列表如

 val myList = List(TypeA(1), TypeB(11), TypeB(12), TypeA(2), TypeB(21), TypeA(3), TypeB(31)) 

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

 myList.slice { x => x.isInstanceOf[TypeA] } 

会交付

 List(List(TypeA(1), TypeB(11), TypeB(12)), List(TypeA(2), TypeB(21)), List(TypeA(3), TypeB(31))) 

在这个例子中,相同的结果将通过

 myList.slice { case TypeA(x) => x < 10 } 

非常感谢。

List已经有一个slice方法 – 它需要一个开始和结束索引之间的元素的子集。 你要找的是重复应用span方法:

 def span(p: (A) ⇒ Boolean): (List[A], List[A]) 

其中logging为:

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

注意:如果谓词p的评估不会导致任何副作用,则c span p相当于(但可能更有效)(c takeWhile p,c dropWhile 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 } 

UPDATE

根据原帖的评论请求,这里是一个丰富的列表,而不是一个独立的方法:

 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 } } 

用于:

 myList.multiSpan(_.isInstanceOf[TypeA]) 

为什么不能使用标准API的分区方法?

例:

 scala> val l = List(3,5,4,6) l: List[Int] = List(3, 5, 4, 6) scala> scala> val (odd,even) = l.partition(_ %2 ==1) odd: List[Int] = List(3, 5) even: List[Int] = List(4, 6) 

举个例子:

 scala> val (typeA,typeB) = myList.partition(_.isInstanceOf[TypeA]) typeA: List[Product with Serializable with Item] = List(TypeA(1), TypeA(2), TypeA(3)) typeB: List[Product with Serializable with Item] = List(TypeB(11), TypeB(12), TypeB(21), TypeB(31)) 

你不是在寻找filter ,哪个(几乎)没有任何调整你的例子?

 $ sbt console scala> trait Item scala> case class TypeA(i: Int) extends Item scala> case class TypeB(i: Int) extends Item scala> val myList = List(TypeA(1), TypeB(11), TypeB(12), TypeA(2), TypeB(21), TypeA(3), TypeB(31)) myList: List[Product with Serializable with Item] = List(TypeA(1), TypeB(11), TypeB(12), TypeA(2), TypeB(21), TypeA(3), TypeB(31)) 

你的第一部作品保持不变:

 scala> myList.filter { x => x.isInstanceOf[TypeA] } res0: List[Product with Serializable with Item] = List(TypeA(1), TypeA(2), TypeA(3)) 

你的第二个需要默认情况下:

 scala> myList.filter { case TypeA(x) => x < 10; case _ => false } res2: List[Product with Serializable with Item] = List(TypeA(1(3)) 

另请参阅collect ,它使用部分函数而不是布尔谓词:

 scala> myList.collect { case z @ TypeA(x) if x < 10 => z } res3: List[TypeA] = List(TypeA(1), TypeA(2), TypeA(3)) 

并可以转换:

 scala> myList.collect { case TypeA(x) if x < 10 => x } res4: List[Int] = List(1, 2, 3)