繁体   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