Scala的for循环推导式何时是惰性的?

15

在Python中,我可以像这样做:

lazy = ((i,j) for i in range(0,10000) for j in range(0,10000))
sum((1 for i in lazy))

这需要一段时间,但内存使用是恒定的。

在 Scala 中相同的结构:

(for(i<-0 to 10000; j<-i+1 to 10000) yield (i,j)).count((a:(Int,Int)) => true)

过了一会儿,我会遇到一个java.lang.OutOfMemoryError错误,尽管它应该被惰性评估。

2个回答

26

Scala 中的 for 推导式本身并不懒惰;它只是一种语法糖*,不会改变你两个范围的组合是急切计算的事实。

如果使用范围的延迟 view,推导式的结果也将是延迟的:

scala> for(i<-(0 to 10000).view; j<-(i+1 to 10000).view) yield (i,j)
res0: scala.collection.SeqView[(Int, Int),Seq[_]] = SeqViewN(...)

scala> res0.count((a: (Int, Int)) => true)
res1: Int = 50005000

这里的懒惰与for-comprehension无关,而是因为当在某种类型的容器上调用flatMapmap(见下文)时,会得到相同类型的容器中的结果。因此,for-comprehension只会保留您放入的任何东西的惰性(或缺乏惰性)。


*就像下面这样:

(0 to 10000).flatMap(i => (i+1 to 10000).map(j => (i, j)))

“something like”,但后面的表达似乎返回了100020001个元素,而不是50005000个。Whiskey tango foxtrot? - Michael Lorton
@Malvolio 谢谢你的注意!j 应该是从 i+1 到 10000,而不是 1 到 10000。现在已经修复了。 - Ben James
哎呀,我应该注意到解决方案,而不仅仅是问题。我一直盯着地图函数,忽略了参数... - Michael Lorton
在2.7版本中,Int.to(Int)不是懒加载的吗? - tstenner

13

懒惰并不是由于for-comprehension,而是由于集合本身。您应该查看集合的严格特性。

但是,对于懒人 :-),这里是一个总结: 迭代器(Iterator)流(Stream)是非严格的,任何集合的view选择的方法也是非严格的。因此,如果您想要懒惰,请确保先.iterator, .view.toStream您的集合。


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