视图和流的区别是什么?

25
在Scala 2.8集合框架中,viewtoStream有什么区别?
3个回答

46

在视图中,每次访问元素时都会重新计算。在流中,元素在被评估后保留不变。

例如:

val doubled = List(1,2,3,4,5,6,7,8,9,10).view.map(_*2)
println(doubled.mkString(" "))
println(doubled.mkString(" "))

将对每个元素重新评估两次映射。一次用于第一个println,另一次用于第二个println。相比之下,

val doubled = List(1,2,3,4,5,6,7,8,9,10).toStream.map(_*2)
println(doubled.mkString(" "))
println(doubled.mkString(" "))

只会将元素加倍一次。

视图就像是创建集合的配方。每当您请求视图的元素时,它都会执行该配方。

流就像是拿着一堆干擦卡片的人。这个人知道如何计算集合的后续元素。您可以向他请求集合的下一个元素,并给您一张写有该元素的卡片和一根系在他手指上的绳子(以帮助他记住)。此外,在他给你一张新卡片之前,他会解开第一根绳子并将其系到新卡片上。

如果您保留第一张卡片(即保留对流的头部的引用),当您请求下一个元素时,您可能最终会用完卡片(即内存),但是如果您不需要返回到第一个元素,则可以切断绳子并将不需要的卡片交还给这个人,然后他可以重复使用它们(它们是干擦卡片)。这就是流如何表示无限序列而不会耗尽内存的方式。


@huynhj 你说得对。我可以更新答案,使其更加清晰明了。 - Geoff Reedy
1
有时候,隐喻可以帮助理解。但在这种情况下,我反而被它搞糊涂了。第一句话就包含了所有的信息。 - huynhjl
1
字符串示例非常令人困惑。 - ziggystar

10

Geoff回答几乎涵盖了所有内容,但我想补充一下: Stream是类似于List的序列,而所有类型的集合(映射、集合、索引序列)都有视图。


我有一个疑问。既然函数是纯的,为什么编译器不使用引用透明或记忆化?为什么视图会重新计算它? - Mahesh Chand

0
另一种解释这个问题的方法是,如果您了解Apache Spark,那么使用流就像缓存Spark数据集,而使用视图就像使用未缓存的数据集,这意味着每次调用它上面的某些操作时,它都会重新评估DAG中的所有内容。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接