Scala:流不偷懒吗? [英] Scala: Streams not acting lazy?
问题描述
我知道流在Scala中应该是延迟评估的序列,但是我认为我受到某种根本性的误解,因为它们似乎比我预期的要热切.
I know streams are supposed to be lazily evaluated sequences in Scala, but I think I am suffering from some sort of fundamental misunderstanding because they seem to be more eager than I would have expected.
在此示例中:
val initial = Stream(1)
lazy val bad = Stream(1/0)
println((initial ++ bad) take 1)
我得到一个java.lang.ArithmeticException
,这似乎是由于零除引起的.我希望bad
永远不会得到评估,因为我只要求从流中获取一个元素.怎么了?
I get a java.lang.ArithmeticException
, which seems to be cause by zero division. I would expect that bad
would never get evaluated since I only asked for one element from the stream. What's wrong?
推荐答案
好的,所以在评论其他答案之后,我认为我也可以将自己的评论变成正确的答案.
OK, so after commenting other answers, I figured I could as well turn my comments into a proper answer.
流确实是惰性的,并且只会按需计算其元素(并且您可以使用#::
逐元素构造流元素,就像::
对于List
一样).例如,以下内容不会引发任何异常:
Streams are indeed lazy, and will only compute their elements on demand (and you can use #::
to construct a stream element by element, much like ::
for List
). By example, the following will not throw any exception:
(1/2) #:: (1/0) #:: Stream.empty
这是因为在应用#::
时,尾部将按名称传递,以便不急切地对其进行评估,而仅在需要时才使用(有关更多信息,请参见ConsWrapper.# ::
,const.apply
和类Cons
细节).
另一方面,头部是通过值传递的,这意味着无论如何(如Senthil所提到的那样),都将始终对它进行热切的评估.这意味着执行以下操作实际上将引发ArithmeticException:
This is because when applying #::
, the tail is passed by name so as to not evaluate it eagerly, but only when needed (see ConsWrapper.# ::
, const.apply
and class Cons
in Stream.scala
for more details).
On the other hand, the head is passed by value, which means that it will always be eagerly evaluated, no matter what (as mentioned by Senthil). This means that doing the following will actually throw a ArithmeticException:
(1/0) #:: Stream.empty
这是一个值得了解的关于流的陷阱.但是,这不是您面临的问题.
It is a gotcha worth knowing about streams. However, this is not the issue you are facing.
在您的情况下,算术异常发生在实例化单个Stream之前.在lazy val bad = Stream(1/0)
中调用Stream.apply
时,会急切地执行该参数,因为它没有被声明为by name参数. Stream.apply
实际上采用vararg参数,并且这些参数必须按值传递.
即使通过名称传递ArithmeticException
,它也会在不久后触发,因为如前所述,Stream的头部始终会被早期评估.
In your case, the arithmetic exception happens before even instantiating a single Stream. When calling Stream.apply
in lazy val bad = Stream(1/0)
, the argument is eagerly executed because it is not declared as a by name parameter. Stream.apply
actually takes a vararg parameter, and those are necessarily passed by value.
And even if it was passed by name, the ArithmeticException
would be triggered shortly after, because as said earlier the head of a Stream is always early evaluated.
这篇关于Scala:流不偷懒吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!