通过查找sentinel值(在scala中)对可迭代项目进行分组

时间:2010-07-12 18:33:53

标签: scala iterator grouping

我有一个来自非常大的文件的行的迭代器,当我移动时需要将它们放入组中。我知道每个组的结束位置,因为每个组的最后一行都有一个标记值。所以基本上我想写一个函数,它接受一个迭代器和一个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)
}

我认为这会有效,我想这很好,但每次都要重新添加哨兵给我一个代码味道。有更好的方法吗?

2 个答案:

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