如何扩展 Scala 列表以启用切片,而不是按显式位置而是按给定的谓词/条件 [英] How to extend a Scala list to enable slicing not by explicit position but by given predicate/condition
问题描述
为了
trait Item
case class TypeA(i: Int) extends Item
case class TypeB(i: Int) extends Item
考虑一个 Scala 项目列表,例如
consider a Scala list of items such as
val myList = List(TypeA(1), TypeB(11), TypeB(12),
TypeA(2), TypeB(21),
TypeA(3), TypeB(31))
目标是定义一个新的 slice
方法,该方法可以应用到 myList
上,并以谓词或条件作为参数;例如
The goal is to define a new slice
method that can be applied onto myList
and which takes a predicate or condition as argument; for instance
myList.slice { x => x.isInstanceOf[TypeA] }
将交付
List(List(TypeA(1), TypeB(11), TypeB(12)),
List(TypeA(2), TypeB(21)),
List(TypeA(3), TypeB(31)))
在这个例子中,相同的结果将通过
In this example, an identical result would be achieved by
myList.slice { case TypeA(x) => x < 10 }
非常感谢.
推荐答案
List
已经有一个 slice
方法 - 它需要开始和结束索引之间的元素子集.您正在寻找的是重复应用 span
方法:
List
already has a slice
method - it takes a subset of elements between a start and end index. What you're looking for is repeated application of the span
method:
def span(p: (A) ⇒ Boolean): (List[A], List[A])
记录为:
根据谓词将此列表拆分为前缀/后缀对.
Splits this list into a prefix/suffix pair according to a predicate.
注意:c span p 等价于(但可能比)(c takeWhile p, c dropWhile p),前提是谓词 p 的计算不会引起任何副作用.
返回:由这个列表的最长前缀组成的一对,其元素都满足p,以及这个列表的其余部分.
returns: a pair consisting of the longest prefix of this list whose elements all satisfy p, and the rest of this list.
通过使用逆谓词和额外的逻辑来确保返回的 List 不为空,您可以通过重复使用此方法来获得所需的内容.
You can get what you need by repeatedly using this method with an inverse predicate, and an extra bit of logic to ensure that none of the returned Lists are empty.
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
}
更新
根据对原始帖子的评论的要求,这是一个丰富列表而不是独立方法的版本:
As requested in comments on the original post, here's a version that enriches list instead of being a standalone method:
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])
这篇关于如何扩展 Scala 列表以启用切片,而不是按显式位置而是按给定的谓词/条件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!