Scala:流不偷懒吗? [英] Scala: Streams not acting lazy?

查看:37
本文介绍了Scala:流不偷懒吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我知道流在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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆