我需要一种可以将Iterator[Char]
拆分为行的方法(由\n
和\r
分隔)
为此,我编写了一个获取迭代器和谓词的通用方法,并在每次谓词为真时拆分迭代器。
这类似于span
,但每次谓词为真时都会拆分,而不仅仅是第一次
这是我的实施:
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行是hasNext
中第二个迭代器的span
,第651行是来自hasNext
的迭代器中的dropWhile
我想我错误地使用了这两个迭代器,但我不明白为什么
答案 0 :(得分:4)
您可以按如下方式简化代码,这似乎可以解决问题:
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
(因此您需要在每次调用iter
时替换next
),只需在原始takeWhile
和dropWhile
上使用iter
{1}}。然后就不需要var
。
我认为原始堆栈溢出的原因是反复调用span
会创建一长串Iterator
s,其hasNext
个方法调用hasNext
个Iterator
其父Iterator
。如果您查看span
的{{3}},可以看到每个hasNext
创建新的迭代器,将BufferedIterator
的调用转发给原始迭代器(通过takeWhile
},这进一步增加了调用堆栈。)
更新咨询了source code似乎虽然我的解决方案似乎有效,但不推荐 - 特别注意:
特别重要的是要注意,除非另有说明, 在调用方法之后,永远不应该使用迭代器。 [...]使用旧迭代器是未定义的,可能会发生更改,并且可能会导致对新迭代器进行更改。
适用于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)
}
}
,但使用流而不是迭代器和递归:
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)
但表现非常糟糕。我相信一定有更好的方法......
更新2:这是一个非常迫切的解决方案,具有更好的性能:
app.service = $provide.service;