我正在將一個專案從 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
有一個愚蠢的方法來修復它,通過將視圖轉換為 seq,然后再回傳到視圖:
val nodes = debts.view
.flatMap { case Debt(from, to, _) => List(from, to) }.toSeq.view
.distinct
.map(name => (name, new Node(name)))
.toMap
但是,這顯然不是很好,因為它會強制收集視圖,而且不得不在型別之間來回切換也非常不優雅。我找到了另一種修復它的方法,使用的是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,但至少在流式傳輸之后的所有操作,無需重構資料結構。
有了這個,它讓我思考為什么我們應該需要一個視圖,因為它們比以前強大得多(即使我可以相信 2.13 已經解決了這個能力引入的其他一些問題)。我尋找答案并找到了提示,但沒有發現足夠全面的內容。以下是我的研究:
- 2.13 中的視圖描述
- StackOverflow:List.view 和 LazyList 有什么區別?
- 外部網站上的另一個比較
可能是我,但即使在閱讀了這些參考資料之后,我也沒有發現使用視圖的明顯優勢,對于大多數用例(如果不是全部)。有比我更開明的嗎?
uj5u.com熱心網友回復:
Scala 2.13 中的惰性序列實際上有 3 種基本可能性:View、Iterator 和 LazyList。
View是最簡單的惰性序列,附加成本非常低。在一般情況下默認使用是很好的,以避免在處理大序列時分配中間結果。
可以多次遍歷視圖(使用 foreach、foldLeft、toMap 等)。每次遍歷都會單獨執行轉換(map、flatMap、filter 等)。因此必須小心避免耗時的轉換,或者只遍歷 View 一次。
迭代器只能遍歷一次。它類似于 Java Streams 或 Python 生成器。Iterator 上的大多數轉換方法都要求您只使用回傳的 Iterator 并丟棄原始物件。
它也像 View 一樣快,并支持更多操作,包括 distinct。
LazyList基本上是一個真正的嚴格結構,可以動態自動擴展。LazyList 會記住所有生成的元素。如果您有一個val帶有 LazyList 的 ,則將為所有生成的元素分配記憶體。但是如果你動態遍歷它并且不存盤在 a 中val,垃圾收集器可以清理遍歷的元素。
Scala 2.12 中的 Stream 比 Views 或 Iterators 慢得多。我不確定這是否適用于 Scala 2.13 中的 LazyList。
所以每個惰性序列都有一些警告:
- 視圖可以多次執行轉換。
- 迭代器只能被消費一次。
- LazyList 可以為所有序列元素分配記憶體。
在您的用例中,我相信 Iterator 是最合適的:
val nodes = debts.iterator
.flatMap { case Debt(from, to, _) => List(from, to) }
.distinct
.map(name => (name, new Node(name)))
.toMap
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/358548.html
標籤:斯卡拉
