为什么这个可迭代的实现会产生堆栈溢出?

时间:2017-07-03 15:45:26

标签: scala

我正在尝试为链表实现迭代。但是这个实现会抛出堆栈溢出错误。我在这里做错了什么?

即使在我调用类上的任何函数之前,可迭代实现也会抛出错误。

我最初想过,可能是因为对nodeValue和tailList使用了def而不是val。但那并没有解决问题。

我试图将迭代器更改为遍历Int而不是LinkedList。我仍然得到同样的错误。 在此先感谢!!

trait LinkedList extends Iterable[LinkedList]{
  def nodeValue: Int
  def tailList: LinkedList
}

class Node(val nodeValue: Int, val tailList: LinkedList) extends LinkedList {

  override def iterator: Iterator[LinkedList] = Iterator
    .iterate(this: LinkedList)(_.tailList)
    .takeWhile(_ != Nil)
}

object Nil extends LinkedList {
  def nodeValue= throw new IllegalAccessException("head of Nil")
  def tailList = throw new IllegalAccessException("tail of Nil")

  override def iterator: Iterator[LinkedList] = Iterator.empty
}

val singleLinkedList = new Node(1,Nil)
val chainedLinkedList = new Node(2,singleLinkedList)

//Printing this first Time
//chainedLinkedList.foreach(println)
  

输出:

java.lang.StackOverflowError
    at scala.collection.AbstractIterable.<init>(LinkedListIterable.sc:50)
    at scala.collection.AbstractSeq.<init>(LinkedListIterable.sc:37)
    at scala.collection.mutable.AbstractSeq.<init>(LinkedListIterable.sc:44)
    at scala.collection.mutable.StringBuilder.<init>(LinkedListIterable.sc:28)
    at scala.collection.mutable.StringBuilder.<init>(LinkedListIterable.sc:45)
    at scala.collection.mutable.StringBuilder.<init>(LinkedListIterable.sc:50)
    at scala.collection.TraversableOnce.mkString(LinkedListIterable.sc:319)
    at scala.collection.TraversableOnce.mkString$(LinkedListIterable.sc:318)
    at #worksheet#.Node.mkString(LinkedListIterable.sc:6)
    at scala.collection.TraversableLike.toString(LinkedListIterable.sc:596)
    at scala.collection.TraversableLike.toString$(LinkedListIterable.sc:596)
    at #worksheet#.Node.toString(LinkedListIterable.sc:6)
    at java.lang.String.valueOf(LinkedListIterable.sc:2990)
    at scala.collection.mutable.StringBuilder.append(LinkedListIterable.sc:196)
    at scala.collection.TraversableOnce.$anonfun$addString$1(LinkedListIterable.sc:355)
    at scala.collection.Iterator.foreach(LinkedListIterable.sc:925)
    at scala.collection.Iterator.foreach$(LinkedListIterable.sc:925)
    at scala.collection.AbstractIterator.foreach(LinkedListIterable.sc:1413)
    at scala.collection.IterableLike.foreach(LinkedListIterable.sc:67)
    at scala.collection.IterableLike.foreach$(LinkedListIterable.sc:66)
    at #worksheet#.Node.foreach(LinkedListIterable.sc:6)
    at scala.collection.TraversableOnce.addString(LinkedListIterable.sc:353)
    at scala.collection.TraversableOnce.addString$(LinkedListIterable.sc:349)
    at #worksheet#.Node.addString(LinkedListIterable.sc:6)
    at scala.collection.TraversableOnce.mkString(LinkedListIterable.sc:319)
    at scala.collection.TraversableOnce.mkString$(LinkedListIterable.sc:318)
    at #worksheet#.Node.mkString(LinkedListIterable.sc:6)
    at scala.collection.TraversableLike.toString(LinkedListIterable.sc:596)
    at scala.collection.TraversableLike.toString$(LinkedListIterable.sc:596)
    at #worksheet#.Node.toString(LinkedListIterable.sc:6)
    at java.lang.String.valueOf(LinkedListIterable.sc:2990)
    at scala.collection.mutable.StringBuilder.append(LinkedListIterable.sc:196)
    at scala.collection.TraversableOnce.$anonfun$addString$1(LinkedListIterable.sc:355)
    at scala.collection.Iterator.foreach(LinkedListIterable.sc:925)
Output exceeds cutoff limit.
  

更新

问题是由@Dima,Iterator提到的,在容器中迭代一些值而不是容器本身。一旦清楚,我修改了代码,运行正常如下。

trait LinkedList extends Iterable[Int]{
  val nodeValue: Int
  val tailList: LinkedList
  override def toString(): String = this.mkString(" -> ")
}

class Node(val nodeValue: Int, val tailList: LinkedList) extends LinkedList {

  override def iterator: Iterator[Int] = Iterator
    .iterate(this: LinkedList)(_.tailList)
    .takeWhile(_ != Nil)
    .map(_.nodeValue)
}

object Nil extends LinkedList {
  lazy val nodeValue= throw new IllegalAccessException("head of Nil")
  lazy val tailList = throw new IllegalAccessException("tail of Nil")

  override def iterator: Iterator[Int] = Iterator.empty
}

val singleLinkedList = new Node(1,Nil)
val chainedLinkedList = new Node(2,singleLinkedList)

//Printing this first Time
chainedLinkedList.foreach(println)

1 个答案:

答案 0 :(得分:3)

嗯,问题是,通常Iterable.iterator会迭代容器中的,而不是实际的容器节点。

这里发生的是,当您在REPL中键入new Node(1, Nil)时,它会尝试打印出结果,并调用Node.toStringIterableiterator上实现以遍历整个容器,并将所有值转换为字符串。

因此,它会调用您的next,获取iterator(它认为是什么)元素,并尝试将其转换为String。但是,toString返回的内容不是存储在节点中的值,而是节点本身,因此,当调用Iterable.toString时,它仍然是相同的iterator,最终调用相同的{{1}},它将返回相同的节点,它将尝试转换为字符串...等无限递归。