rec*_*ive 14 scala stream lazy-evaluation
我知道在Scala中流应该是懒惰的评估序列,但我认为我正遭受某种基本的误解,因为它们似乎比我预期的更加渴望.
在这个例子中:
val initial = Stream(1)
lazy val bad = Stream(1/0)
println((initial ++ bad) take 1)
Run Code Online (Sandbox Code Playgroud)
我得到了一个java.lang.ArithmeticException,这似乎是零分裂造成的.我希望bad永远不会得到评估,因为我只要求流中的一个元素.怎么了?
Dao*_*Wen 21
Streams懒惰这一事实并没有改变方法论据被急切评估的事实.
Stream(1/0)扩展到Stream.apply(1/0).语言的语义要求在调用方法之前评估参数(因为该Stream.apply方法不使用按名称调用参数),因此它会尝试计算1/0作为参数传递给Stream.apply方法,这会导致您的ArithmeticException .
有几种方法可以使这个工作.由于您已经声明bad为a lazy val,最简单的可能是使用也是惰性的#:::流连接运算符来避免强制求值:
val initial = Stream(1)
lazy val bad = Stream(1/0)
println((initial #::: bad) take 1)
// => Stream(1, ?)
Run Code Online (Sandbox Code Playgroud)
Rég*_*les 21
好的,所以在评论其他答案之后,我想我也可以将我的评论转化为正确的答案.
流确实是懒惰,只会计算其需求的元素(你可以用#::通过元素构建一个流元素,就像::对List).例如,以下内容不会抛出任何异常:
(1/2) #:: (1/0) #:: Stream.empty
Run Code Online (Sandbox Code Playgroud)
这是因为申请时#::,尾部由名字传递,从而不会急于评价它,但仅在需要时(见ConsWrapper.# ::,const.apply和类Cons中Stream.scala有详细介绍).另一方面,头部通过值传递,这意味着无论如何都会对其进行热切评估(如Senthil所述).这意味着执行以下操作实际上会抛出ArithmeticException:
(1/0) #:: Stream.empty
Run Code Online (Sandbox Code Playgroud)
这是一个值得了解流的问题.但是,这不是您面临的问题.
在您的情况下,算术异常甚至在实例化单个Stream之前发生.在调用Stream.apply时lazy val bad = Stream(1/0),会急切地执行该参数,因为它未被声明为by name参数.Stream.apply实际上需要一个vararg参数,并且这些参数必须按值传递.即使它是通过名字传递的,ArithmeticException也会在不久之后触发,因为如前所述,Stream的负责人总是会进行早期评估.
| 归档时间: |
|
| 查看次数: |
2897 次 |
| 最近记录: |