【发布时间】:2021-04-24 19:22:14
【问题描述】:
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 有什么建议?
【问题讨论】:
-
自从
new Foo {}使用反射?不管怎样,看看 Iterator 上的unfold方法。 -
好点,我误解了一条错误消息。
-
您的访问者与代码中的完全一样吗?因为这可以通过
LazyList,.flatMap和递归来实现。 -
@mateusz-kubuszok 原始访问者非常相似(参见here)。由于它在很多地方都被调用,所以我一直在寻找不修改它的解决方案。我仍然看不到
LazyList会如何阻止分配。您的意思是LazyList上的迭代器永远不会分配完整列表? -
事实上,Iterator 和 Traversable 只是接口,它不依赖于知道前面的所有值。当您执行
Iterator.from(0).drop(1000000).take(4).toList时,您不会预先分配 1000000 个值。您只在需要时分配每个值。
标签: scala migration iterable visitor-pattern scala-2.13