Scala 可遍历递归节点结构

Scala 可遍历递归节点结构,scala,foreach,stack-overflow,tostring,case-class,Scala,Foreach,Stack Overflow,Tostring,Case Class,我试图在节点结构上实现递归遍历: sealed class Node(subnodes: Traversable[Node]) extends Traversable[Node] { def foreach[U](f: Node => U) { f(this) subnodes foreach f } } case class Atom(id: String) extends Node(Nil) case class Molecule(atoms: List[N

我试图在节点结构上实现递归遍历:

sealed class Node(subnodes: Traversable[Node]) extends Traversable[Node] {
  def foreach[U](f: Node => U) {
    f(this)
    subnodes foreach f
  }
}

case class Atom(id: String) extends Node(Nil)

case class Molecule(atoms: List[Node]) extends Node(atoms)
对类似原子的元素调用
toString
(“测试”)。toString会导致堆栈溢出:

Exception in thread "main" java.lang.StackOverflowError
at java.lang.System.arraycopy(Native Method)
at java.lang.String.getChars(Unknown Source)
at java.lang.AbstractStringBuilder.append(Unknown Source)
at java.lang.StringBuilder.append(Unknown Source)
at scala.collection.mutable.StringBuilder.append(StringBuilder.scala:197)
at scala.collection.TraversableOnce$class.addString(TraversableOnce.scala:297)
at Node.addString(Fail.scala:1)
at scala.collection.TraversableOnce$class.mkString(TraversableOnce.scala:263)
at Node.mkString(Fail.scala:1)
at scala.collection.TraversableLike$class.toString(TraversableLike.scala:615)
at Node.toString(Fail.scala:1)
at java.lang.String.valueOf(Unknown Source)
at scala.collection.mutable.StringBuilder.append(StringBuilder.scala:187)
at scala.collection.TraversableOnce$$anonfun$addString$1.apply(TraversableOnce.scala:300)
[...]
at Node.foreach(Fail.scala:3)
at scala.collection.TraversableOnce$class.addString(TraversableOnce.scala:298)
at Node.addString(Fail.scala:1)
at scala.collection.TraversableOnce$class.mkString(TraversableOnce.scala:263)
at Node.mkString(Fail.scala:1)
at scala.collection.TraversableLike$class.toString(TraversableLike.scala:615)
at Node.toString(Fail.scala:1)
at java.lang.String.valueOf(Unknown Source)
at scala.collection.mutable.StringBuilder.append(StringBuilder.scala:187)
at scala.collection.TraversableOnce$$anonfun$addString$1.apply(TraversableOnce.scala:300)
注意,我没有在任何地方显式调用foreach。那么,为什么会出现堆栈溢出


我用一个额外的
TraversableNode
类和从
Node
TraversableNode
的隐式转换解决了这个特殊问题,但我仍然想知道是什么导致了堆栈溢出。谢谢。

通常情况下,case类上的
toString
方法会打印类名及其参数列表(第一个参数列表中的参数)。如果您的case类扩展了一个具有显式定义的
toString
的类,则此规则有一个例外

您的
节点
扩展了
可遍历的[Node]
。请注意,它没有编译器生成的case类
toString
,如其AST(
scalac-Xprint:cleanup
)所示:

case类Atom使用可序列化的产品扩展节点{
....
重写def hashCode():Int=ScalaRunTime.this.\u hashCode(Atom.this);
override def equals(x$1:Any):Boolean=Atom.this.eq(x$1.asInstanceOf[Object]())。| |(x$1.isInstanceOf[Atom]()&&({
val Atom$1:Atom=x$1.asInstanceOf[Atom]();
Atom.this.id()。==(Atom$1.id())&&(Atom$1.canEqual(Atom.this))
}))
};
上面,编译器“仅”生成
hashCode
等于
toString
可遍历的
继承


来自
集合
包的
travelable
trait有一个
toString
方法,该方法是根据
foreach
定义的。调用
toString
会使
foreach
在节点本身上迭代(与
f(this)
一致),从而产生一个无限循环。

原子上调用
toString
时,您得到的是
可遍历的
,如下所述:

默认情况下,此字符串由该字符串的
stringPrefix
组成 集合,后跟所有元素,元素之间用逗号分隔并封闭 括号内

“后跟所有元素”部分通过调用集合的
foreach
来实现。由于您的
foreach
首先命中
节点本身,因此您会立即陷入一个无休止的循环

case class Atom extends Node with Product with Serializable {
   ....
    override <synthetic> def hashCode(): Int = ScalaRunTime.this._hashCode(Atom.this);
    override <synthetic> def equals(x$1: Any): Boolean = Atom.this.eq(x$1.asInstanceOf[Object]()).||(x$1.isInstanceOf[Atom]().&&({
      <synthetic> val Atom$1: Atom = x$1.asInstanceOf[Atom]();
      Atom.this.id().==(Atom$1.id()).&&(Atom$1.canEqual(Atom.this))
    }))
  };