簡體   English   中英

Scala中的惰性評估有哪些良好的用例?

[英]What are some good use cases of lazy evaluation in Scala?

在處理大型館藏時,我們通常會聽到“惰性評估”一詞。 我想更好地說明嚴格評估和惰性評估之間的區別,因此我嘗試了以下示例-從列表中獲取前兩個偶數:

scala> var l = List(1, 47, 38, 53, 51, 67, 39, 46, 93, 54, 45, 33, 87)
l: List[Int] = List(1, 47, 38, 53, 51, 67, 39, 46, 93, 54, 45, 33, 87)

scala> l.filter(_ % 2 == 0).take(2)
res0: List[Int] = List(38, 46)

scala> l.toStream.filter(_ % 2 == 0).take(2)
res1: scala.collection.immutable.Stream[Int] = Stream(38, ?)

我注意到當我使用toStream ,我得到了Stream(38, ?) toStream Stream(38, ?) 什么是“?” 是這里嗎 這與懶惰評估有關嗎?

另外,什么是延遲評估的好例子,什么時候應該使用它,為什么?

使用惰性集合的一個好處是“保存”內存,例如在映射到大型數據結構時。 考慮一下:

val r =(1 to 10000)
   .map(_ => Seq.fill(10000)(scala.util.Random.nextDouble))
   .map(_.sum)
   .sum

並使用惰性評估:

val r =(1 to 10000).toStream
   .map(_ => Seq.fill(10000)(scala.util.Random.nextDouble))
   .map(_.sum)
   .sum

第一條語句將生成大小為10000的10000 Seq並將其保留在內存中,而在第二種情況下,一次只需要在內存中存在一個Seq ,因此它的速度要快得多...

另一個用例是實際只需要一部分數據時。 我經常將惰性集合與taketakeWhile等一起使用

讓我們來看一個現實生活中的場景-您沒有一個列表,而是擁有一個大型日志文件,您希望提取其中包含“成功”的前10行。

直接的解決方案是逐行讀取文件,一旦包含“成功”的行,則將其打印並繼續下一行。

但是因為我們喜歡函數式編程,所以我們不想使用傳統的循環。 相反,我們希望通過組合功能來實現我們的目標。

第一次嘗試:

Source.fromFile("log_file").getLines.toList.filter(_.contains("Success")).take(10)

讓我們嘗試了解這里實際發生的情況:

  1. 我們閱讀了整個文件

  2. 過濾相關行

  3. 拿了前10個元素

如果嘗試打印Source.fromFile("log_file").getLines.toList ,則將獲取整個文件,這顯然是浪費,因為並非所有行都與我們相關。

為什么我們得到所有行,然后才執行過濾? 這是因為List是嚴格的數據結構,因此當我們調用toList ,它將立即求值,並且僅在擁有全部數據之后才應用過濾。

幸運的是,Scala提供了惰性數據結構, 是其中之一:

Source.fromFile("log_file").getLines.toStream.filter(_.contains("Success")).take(10)

為了演示差異,讓我們嘗試:

Source.fromFile("log_file").getLines.toStream

現在我們得到類似:

Scala.collection.immutable.Stream[Int] = Stream(That's the first line, ?)

toStream 僅計算一個元素 -文件的第一行。 下一個元素由“?”表示,表示流尚未評估下一個元素,這是因為toStreamlazy function ,並且僅在使用時評估下一項。

現在,在應用過濾器功能之后,它將開始讀取下一行,直到獲得包含“成功”的第一行:

> var res = Source.fromFile("log_file").getLines.toStream.filter(_.contains("Success"))
Scala.collection.immutable.Stream[Int] = Stream(First line contains Success!, ?)

現在,我們將take功能。 仍然沒有執行任何操作,但是它知道應該選擇10行,因此在我們使用結果之前它不會評估:

res foreach println

最后,我們現在打印res ,正如我們期望的那樣,我們將獲得一個包含前10行的Stream。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM