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

Ste*_*eve 5 grouping iterator scala

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

请注意,我希望每个组的末尾都包含标记项.这是我目前的解决方案:

def groups[T](iter: Iterator[T], sentinel: T) = new Iterator[Seq[T]] {                   
  def hasNext = iter.hasNext
  def next = iter.takeWhile(_ != sentinel).toList ++ List(sentinel)
}
Run Code Online (Sandbox Code Playgroud)

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

Mit*_*ins 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
 }
}
Run Code Online (Sandbox Code Playgroud)

或者,递归地:

  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]())
    }
  }
Run Code Online (Sandbox Code Playgroud)


Lan*_*dei 3

丑陋,但应该比您的解决方案性能更高:

  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
  }
Run Code Online (Sandbox Code Playgroud)