Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/user-interface/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Scala 用谓词拆分迭代器_Scala_Iterator - Fatal编程技术网

Scala 用谓词拆分迭代器

Scala 用谓词拆分迭代器,scala,iterator,Scala,Iterator,我需要一个可以将迭代器[Char]拆分为行的方法(由\n和\r分隔) 为此,我编写了一个通用方法,它获取一个迭代器和一个谓词,并在每次谓词为true时分割迭代器。 这类似于span,但将在每次谓词为true时分割,而不仅仅是第一次 这是我的实现: def iterativeSplit[T](iterO: Iterator[T])(breakOn: T => Boolean): Iterator[List[T]] = new Iterator[List[T]] { private v

我需要一个可以将
迭代器[Char]
拆分为行的方法(由
\n
\r
分隔)

为此,我编写了一个通用方法,它获取一个迭代器和一个谓词,并在每次谓词为true时分割迭代器。 这类似于
span
,但将在每次谓词为true时分割,而不仅仅是第一次

这是我的实现:

def iterativeSplit[T](iterO: Iterator[T])(breakOn: T => Boolean): Iterator[List[T]] = 
 new Iterator[List[T]] {
  private var iter = iterO
  def hasNext = iter.hasNext

  def next = {
    val (i1,i2) = iter.span(el => !breakOn(el))
    val cur = i1.toList
    iter = i2.dropWhile(breakOn)
    cur
  }
}.withFilter(l => l.nonEmpty)
它在小的输入上运行良好,但在大的输入上运行非常慢,有时会出现堆栈溢出异常

以下是重新创建问题的代码:

val iter = ("aaaaaaaaabbbbbbbbbbbccccccccccccc\r\n" * 10000).iterator
iterativeSplit(iter)(c => c == '\r' || c == '\n').length
运行期间的堆栈跟踪为:

... 
at scala.collection.Iterator$$anon$1.hasNext(Iterator.scala:847)
at scala.collection.Iterator$$anon$19.hasNext(Iterator.scala:615)
at scala.collection.Iterator$$anon$1.hasNext(Iterator.scala:847)
at scala.collection.Iterator$$anon$18.hasNext(Iterator.scala:591)
at scala.collection.Iterator$$anon$1.hasNext(Iterator.scala:847)
at scala.collection.Iterator$$anon$19.hasNext(Iterator.scala:615)
at scala.collection.Iterator$$anon$1.hasNext(Iterator.scala:847)
at scala.collection.Iterator$$anon$18.hasNext(Iterator.scala:591)
at scala.collection.Iterator$$anon$1.hasNext(Iterator.scala:847)
at scala.collection.Iterator$$anon$19.hasNext(Iterator.scala:615)
at scala.collection.Iterator$$anon$1.hasNext(Iterator.scala:847)
at scala.collection.Iterator$$anon$18.hasNext(Iterator.scala:591)
at scala.collection.Iterator$$anon$1.hasNext(Iterator.scala:847)
...
查看源代码(我使用的是scala 2.10.4) 第591行是
span
中第二个迭代器的
hasNext
,第651行是
dropWhile
中迭代器的
hasNext


我猜这两个迭代器使用得不正确,但我不明白为什么可以将代码简化为以下内容,这似乎解决了问题:

  def iterativeSplit2[T](iter: Iterator[T])(breakOn: T => Boolean): Iterator[List[T]] =
    new Iterator[List[T]] {
      def hasNext = iter.hasNext

      def next = {
        val cur = iter.takeWhile(!breakOn(_)).toList
        iter.dropWhile(breakOn)
        cur
      }
    }.withFilter(l => l.nonEmpty)   
与其使用
span
(因此每次调用
next
时都需要更换
iter
),只需在原始
iter
上使用
takeWhile
dropWhile
。那么就不需要
var

我认为最初堆栈溢出的原因是重复调用
span
创建了一个长链的
Iterator
s,每个
hasNext
方法调用其父
Iterator
hasNext
。如果查看for
迭代器
,可以看到每个
span
都会创建新的迭代器,将对
hasNext
的调用转发到原始迭代器(通过
buffereditor
,这会进一步增加调用堆栈)

更新在咨询了之后,尽管我的上述解决方案似乎有效,但不建议使用它-具体请参见:

特别重要的是要注意,除非另有说明, 在调用迭代器上的方法后,决不能使用迭代器。 […]使用旧迭代器是未定义的,可能会发生更改,并且可能会导致对新迭代器的更改

适用于
takeWhile
dropWhile
(和
span
),但不适用于
next
hasNext

可以像在原始解决方案中一样使用
span
,但可以使用流而不是迭代器和递归:

  def split3[T](s: Stream[T])(breakOn: T => Boolean): Stream[List[T]] = s match {
    case Stream.Empty => Stream.empty
    case s => {
      val (a, b) = s.span(!breakOn(_))
      a.toList #:: split3(b.dropWhile(breakOn))(breakOn)
    }
  }
但是表演非常糟糕。我相信一定有更好的方法

更新2:这里有一个非常重要的解决方案,它具有更好的性能:

import scala.collection.mutable.ListBuffer

  def iterativeSplit4[T](iter: Iterator[T])(breakOn: T => Boolean): Iterator[List[T]] =
    new Iterator[List[T]] {
      val word = new ListBuffer[T]
      def hasNext() = iter.hasNext

      def next = {
        var looking = true
        while (looking) {
          val c = iter.next
          if (breakOn(c)) looking = false
          else word += c
        }
        val w = word.toList
        word.clear()
        w
      }
    }.withFilter(_.nonEmpty)

做得好,谢谢。现在也不需要使用
dropWhile
,因为过滤器会处理断开的元素。我已经更新了我的答案,因为我意识到我的原始解决方案使用了文档中强烈反对的技术。这很好,谢谢!为了防止在拆分迭代器上使用
toList
时出错,我添加了
while(looking&&iter.hasNext)