migration guide to Scala 2.13说明 Traversable
已被删除,应该使用 Iterable
代替。对于一个项目来说,这个变化特别烦人,它使用访问者在树的 Node
类中实现 foreach
方法:
case class Node(val subnodes: Seq[Node]) extends Traversable[Node] {
override def foreach[A](f: Node => A) = Visitor.visit(this, f)
}
object Visitor {
def visit[A](n: Node, f: Node => A): Unit = {
f(n)
for (sub <- n.subnodes) {
visit(sub, f)
}
}
}
object Main extends App {
val a = Node(Seq())
val b = Node(Seq())
val c = Node(Seq(a, b))
for (Node(subnodes) <- c) {
Console.println("Visiting a node with " + subnodes.length + " subnodes")
}
}
输出:
Visiting a node with 2 subnodes
Visiting a node with 0 subnodes
Visiting a node with 0 subnodes
迁移到 Scala 2.13 的一个简单修复是首先将访问的元素存储在 remaining
缓冲区中,然后使用该缓冲区返回迭代器:
import scala.collection.mutable
import scala.language.reflectiveCalls
case class Node(val subnodes: Seq[Node]) extends Iterable[Node] {
override def iterator: Iterator[Node] = {
val remaining = mutable.Queue.empty[Node]
Visitor.visit(this, item => iterator.remaining.enqueue(item))
remaining.iterator
}
}
// Same Visitor object
// Same Main object
这个解决方案的缺点是它引入了新的分配给 GC 带来压力,因为被访问元素的数量通常非常大。
您对如何使用现有访问者但不引入新分配从 Traversable
迁移到 Iterable
有什么建议吗?
最佳答案
如您所见,您需要扩展 Iterable
而不是 Traversable
。你可以这样做:
case class Node(name: String, subnodes: Seq[Node]) extends Iterable[Node] {
override def iterator: Iterator[Node] = Iterator(this) ++ subnodes.flatMap(_.iterator)
}
val a = Node("a", Seq())
val b = Node("b", Seq())
val c = Node("c", Seq(a, b))
val d = Node("d", Seq(c))
for (node@Node(name, _) <- d) {
Console.println("Visiting node " + name + " with " + node.subnodes.length + " subnodes")
}
输出:
Visiting node d with 1 subnodes
Visiting node c with 2 subnodes
Visiting node a with 0 subnodes
Visiting node b with 0 subnodes
然后你可以做更多的操作比如:
d.count(_.subnodes.length > 1)
代码运行于 Scastie .
关于scala - 将使用访问者的 Traversable 迁移到 Scala 2.13 中的 Iterable,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65809358/