我有一个来自非常大的文件的行的迭代器,当我移动时需要将它们放入组中。我知道每个组的结束位置,因为每个组的最后一行都有一个标记值。所以基本上我想写一个函数,它接受一个迭代器和一个sentinel值,并返回一个组的迭代器,每个组都以sentinel值终止。类似的东西:
scala> groups("abc.defg.hi.jklmn.".iterator, '.')
res1: Iterator[Seq[Char]] = non-empty iterator
scala> groups("abc.defg.hi.jklmn.".iterator, '.').toList
res19: List[Seq[Char]] = List(List(a, b, c, .), List(d, e, f, g, .), List(h, i, .), List(j, k, l, m, n, .))
请注意,我希望每个组的末尾都包含Sentinel项目。这是我目前的解决方案:
def groups[T](iter: Iterator[T], sentinel: T) = new Iterator[Seq[T]] {
def hasNext = iter.hasNext
def next = iter.takeWhile(_ != sentinel).toList ++ List(sentinel)
}
我认为这会有效,我想这很好,但每次都要重新添加哨兵给我一个代码味道。有更好的方法吗?
答案 0 :(得分:5)
比你的可读性差,但是当最后一组没有终止哨兵值时更“正确”:
def groups[T](iter: Iterator[T], sentinel: T) = new Iterator[Seq[T]] {
def hasNext = iter.hasNext
def next: Seq[T] = {
val builder = scala.collection.mutable.ListBuffer[T]()
while (iter.hasNext) {
val x = iter.next
builder.append(x)
if (x == sentinel) return builder
}
builder
}
}
或者,递归地:
def groups[T](iter: Iterator[T], sentinel: T) = new Iterator[Seq[T]] {
def hasNext = iter.hasNext
def next: Seq[T] = {
@scala.annotation.tailrec
def build(accumulator: ListBuffer[T]): Seq[T] = {
val v = iter.next
accumulator.append(v)
if (v == sentinel || !iter.hasNext) => accumulator
else build(accumulator)
}
build(new ListBuffer[T]())
}
}
答案 1 :(得分:2)
丑陋,但应该比你的解决方案更高效:
def groups[T](iter: Iterator[T], sentinel: T) = new Iterator[Seq[T]] {
def hasNext = iter.hasNext
def next = iter.takeWhile{
var last = null.asInstanceOf[T]
c => { val temp = last; last = c; temp != sentinel}
}.toList
}