我最近注意到这个由于lazy init导致Scala Iterator ++中出现StackOverFlowError的错误。这是出现错误的代码。
var lines = Source.fromFile("file").getLines()
var line = lines.next()
lines = Array(line).toIterator ++ lines
lines.foreach { println(_) }
System.exit(0)
我得到的是
Exception in thread "main" java.lang.StackOverflowError
at scala.collection.Iterator$JoinIterator.hasNext(Iterator.scala:219)
at scala.collection.Iterator$JoinIterator.hasNext(Iterator.scala:219)
at scala.collection.Iterator$JoinIterator.hasNext(Iterator.scala:219)
at scala.collection.Iterator$JoinIterator.hasNext(Iterator.scala:219)
...
它应该由scala源中的这一行引起(scala.collection.Iterator.scala:208)
lazy val rhs: Iterator[A] = that.toIterator
由于rhs是一个惰性初始值,当使用迭代器时,名称“lines”指的是已经改变,并引起循环引用,这会导致错误。
我注意到this post在2013年谈到了这个问题。但是它似乎还没有完全修复。我正在从Maven Repo运行Scala 2.11.8。
我的问题:我可以重命名迭代器,例如“lines2”以避免这个bug,但这是解决问题的唯一方法吗?我觉得使用“线条”这个名字更自然,如果可能的话也不想放弃它。
答案 0 :(得分:2)
如果您想使用相同的Iterator
重新加载var
,这似乎有效。 [经测试2.11.7和2.12.1]
scala> var lines = io.Source.fromFile("file.txt").getLines()
lines: Iterator[String] = non-empty iterator
scala> var line = lines.next()
line: String = this,that,other,than
scala> lines = Iterator(line +: lines.toSeq:_*)
lines: Iterator[String] = non-empty iterator
scala> lines.foreach(println)
this,that,other,than
here,there,every,where
但是使用BufferedIterator
可能更有意义,可以在其上调用head
来查看下一个元素而不使用它。
<强>解释强>
lines.toSeq
&lt; - 将Iterator[String]
转换为Seq[String]
(REPL会将此显示为Stream
,但这是因为REPL必须编译并代表每个输入线分开。)
line +: lines.toSeq
&lt; - 创建一个新的Seq[String]
,line
作为第一个元素(即前置)
(line +: lines.toSeq:_*)
&lt; - 将单个Seq[T]
转换为可以传递给Iterator.apply()
方法的参数列表。 @ som-snytt巧妙地指出,这可以简化为(line +: lines.toSeq).iterator
BufferedIterator
示例
scala> var lines = io.Source.fromFile("file.txt").getLines.buffered
lines: scala.collection.BufferedIterator[String] = non-empty iterator
^^^^^^^^^^^^^^^^^^^^^^^^ <-- note the type
scala> lines.head
res5: String = this,that,other,than
scala> lines foreach println
this,that,other,than
here,there,every,where
答案 1 :(得分:0)
简单捕获:
scala> var lines = Iterator.continually("x")
lines: Iterator[String] = non-empty iterator
scala> lines = { val z = lines ; Iterator.single("y") ++ z }
lines: Iterator[String] = non-empty iterator
scala> lines.next
res0: String = y
scala> lines.next
res1: String = x