如何扩展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项目列表,例如
val myList = List(TypeA(1),TypeB(11),TypeB(12),
TypeA(2),TypeB 21),
TypeA(3),TypeB(31))
目标是定义一个可以应用到 myList
并且以谓词或条件作为参数的新的 slice
方法;例如
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}
非常感谢。
列表
已经有一个 slice
方法 - 开始和结束索引。您要查找的是重复应用 span
方法:
def span(p:(A)⇒Boolean):(List [A],List [A])
其中记录为:
根据谓词将此列表拆分为前缀/后缀对。
注意:c span p等价于(但可能比)更有效(c takeWhile p,c dropWhile p),只要谓词p的求值不会导致任何side-
返回一个由元素都满足p的列表中最长的前缀组成的对,其余的此列表。
您可以通过反复使用此方法获得所需的反谓谓词和额外的逻辑位,以确保没有返回的列表是空的。
import annotation.tailrec
def multiSpan [A] (xs:List [A])(splitOn:(A)=> Boolean):List [List [A]] = {
@tailrec
def loop :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)
} xs,Nil).reverse
}
UPDATE p>
根据对原始帖子的评论请求,这里有一个版本丰富列表,而不是一个独立的方法:
隐含类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)
} list,Nil).reverse
}
}
p>
myList.multiSpan(_。isInstanceOf [TypeA])
For
trait Item
case class TypeA(i: Int) extends Item
case class TypeB(i: Int) extends Item
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))
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] }
would deliver
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 }
Many Thanks.
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])
Which is documented as:
Splits this list into a prefix/suffix pair according to a predicate.
Note: c span p is equivalent to (but possibly more efficient than) (c takeWhile p, c dropWhile p), provided the evaluation of the predicate p does not cause any side-effects.
returns: a pair consisting of the longest prefix of this list whose elements all satisfy p, and the rest of this 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
}
UPDATE
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
}
}
Use as:
myList.multiSpan(_.isInstanceOf[TypeA])
这篇关于如何扩展Scala列表以启用切片不是通过显式位置,而是通过给定的谓词/条件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!