我想出了一个针对以下目标的解决方案:
- 是通用的:您应该能够
Array
像 a 一样拆分 aVector
和Char
s 的集合,就像任意对象的集合一样
- 保留输入的类型:an
Array[A]
在 an中拆分Array[Array[A]]
,aVector[A]
在 a 中拆分Vector[Vector[A]]
- 允许在需要时使用惰性方法(通过
Iterator
)
- 在大多数情况下公开一个紧凑的接口(只需
split
在您的集合上调用一个方法)
在开始解释之前,请注意您可以在 Scastie 上使用下面的代码。
第一步是实现一个分Iterator
块你的集合:
import scala.language.higherKinds
import scala.collection.generic.CanBuildFrom
final class Split[A, CC[_]](delimiter: A => Boolean, as: CC[A])(
implicit view: CC[A] => Seq[A], cbf: CanBuildFrom[Nothing, A, CC[A]])
extends Iterator[CC[A]] {
private[this] var it: Iterator[A] = view(as).iterator
private def skipDelimiters() = {
it = it.dropWhile(delimiter)
}
skipDelimiters()
override def hasNext: Boolean = it.hasNext
override def next(): CC[A] = {
val builder = cbf()
builder ++= it.takeWhile(!delimiter(_))
skipDelimiters()
builder.result()
}
}
我使用谓词而不是值来更灵活地拆分集合的方式,尤其是在拆分非标量值(如Char
s)的集合时。
我正在对集合类型使用隐式视图,以便能够将其应用于可以被视为Seq
(如Vector
s 和Array
s)的所有类型的集合,并且CanBuildFrom
能够构建我收到的集合的确切类型作为输入。
的实现Iterator
只是确保删除分隔符并将其余部分分块。
我们现在可以使用 animplicit class
来提供一个友好的接口并将该split
方法添加到所有集合中,都允许将谓词或值定义为分隔符:
final implicit class Splittable[A, CC[_]](val as: CC[A])(implicit ev1: CC[A] => Seq[A], ev2: CanBuildFrom[Nothing, A, CC[A]], ev3: CanBuildFrom[Nothing, CC[A], CC[CC[A]]]) {
def split(delimiter: A => Boolean): CC[CC[A]] = new Split(as)(delimiter).to[CC]
def split(delimiter: A): CC[CC[A]] = new Split(as)(_ == delimiter).to[CC]
}
现在您可以在收集Char
s时自由使用您的方法
val a = Array('a', 'b', '\n', 'c', 'd', 'e', '\n', 'g', '\n')
val b = List('\n', '\n', '\n')
val c = Vector('\n', 'c', 'd', 'e', '\n', 'g', '\n')
val d = Array('a', 'b', 'c', 'd', 'e', 'g', '\n')
val e = Array('a', 'b', 'c', 'd', 'e', 'g', '\n')
a.split('\n')
b.split('\n')
c.split('\n')
d.split('\n')
e.split('\n')
和任意对象:
final case class N(n: Int, isDelimiter: Boolean)
Vector(N(1, false), N(2, false), N(3, true), N(4, false), N(5, false)).split(_.isDelimiter)
请注意,通过直接使用迭代器,您使用的是惰性方法,因为您可以查看是否向next
方法添加了调试打印并尝试执行以下操作:
new Split(Vector('\n', 'c', 'd', 'e', '\n', 'g', '\n'))(_ == '\n'}).take(1).foreach(println)
如果您愿意,您可以添加几个方法来Splittable
返回 an Iterator
,这样您也可以直接通过它公开惰性方法。