gpt4 book ai didi

Scala 2.13 View 与 LazyList

转载 作者:行者123 更新时间:2023-12-05 01:55:59 27 4
gpt4 key购买 nike

我正在将一个项目从 Scala 2.12.1 迁移到 2.13.6,发现 SeqView#flatMap 现在返回一个 View,它没有distinct 方法。因此,我有一段代码不再编译:

val nodes = debts.view
.flatMap { case Debt(from, to, _) => List(from, to) }
.distinct
.map(name => (name, new Node(name)))
.toMap

有一个愚蠢的方法来修复它,通过将 View 转换为序列然后再返回到 View :

val nodes = debts.view
.flatMap { case Debt(from, to, _) => List(from, to) }.toSeq.view
.distinct
.map(name => (name, new Node(name)))
.toMap

但是,这显然不是很好,因为它强制收集 View ,而且必须在类型之间来回切换也非常不优雅。我找到了另一种修复它的方法,就是使用 LazyList:

val nodes = debts.to(LazyList)
.flatMap { case Debt(from, to, _) => List(from, to) }
.distinct
.map(name => (name, new Node(name)))
.toMap

现在这就是我想要的,它基本上表现得像 Java 流。当然,一些操作有 O(n) 的内存使用,比如 distinct,但至少所有操作之后都被流式处理,而无需重建数据结构。

有了这个,它让我思考为什么我们应该永远需要一个 View ,因为它们比以前强大得多(即使我相信 2.13 已经解决了这个功能引入的一些其他问题)。我寻找答案并找到了提示,但没有找到足够全面的提示。以下是我的研究:

可能是我,但即使在阅读了这些引用资料之后,对于大多数(如果不是全部)用例,我也没有发现使用 View 有明显的好处。还有比我更开明的吗?

最佳答案

在 Scala 2.13 中实际上有 3 种基本的惰性序列可能性:View、Iterator 和 LazyList。

View 是最简单的惰性序列,附加成本非常低。一般情况下最好使用默认值,以避免在处理大型序列时分配中间结果。

可以多次遍历 View (使用 foreach、foldLeft、toMap 等)。每次遍历都会单独执行转换(map、flatMap、filter 等)。因此必须小心避免耗时的转换,或者只遍历 View 一次。

Iterator 只能被遍历一次。它类似于 Java Streams 或 Python 生成器。 Iterator 上的大多数转换方法都要求您仅使用返回的 Iterator 并丢弃原始对象。

它也像 View 一样快速,并且支持更多操作,包括 distinct。

LazyList 基本上是一个真正的严格结构,可以在运行时自动扩展。 LazyList 内存所有生成的元素。如果你有一个带有 LazyList 的 val,内存将被分配给所有生成的元素。但是,如果您即时遍历它并且不将其存储在 val 中,则垃圾收集器可以清理遍历的元素。

Scala 2.12 中的流比 View 或迭代器慢得多。我不确定这是否适用于 Scala 2.13 中的 LazyList。


所以每个惰性序列都有一些警告:

  • View 可以多次执行转换。
  • 迭代器只能使用一次。
  • LazyList可以为所有的序列元素分配内存。

我相信在您的用例中,最合适的是 Iterator:

val nodes = debts.iterator
.flatMap { case Debt(from, to, _) => List(from, to) }
.distinct
.map(name => (name, new Node(name)))
.toMap

关于Scala 2.13 View 与 LazyList,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69997294/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com