斯卡拉的懒惰的论点:他们是如何工作的?

在parsing器组合器库的文件Parsers.scala(Scala 2.9.1)中,我似乎遇到了一个不太为人所知的名为“lazy arguments”的Scala特性。 这是一个例子:

def ~ [U](q: => Parser[U]): Parser[~[T, U]] = { lazy val p = q // lazy argument (for(a <- this; b <- p) yield new ~(a,b)).named("~") } 

很显然,这里有一些事情将名称参数赋值给懒惰的val p

到目前为止,我还没有能够解决这个问题,为什么它是有用的。 谁能帮忙?

每次请求时都会调用名称参数。 懒惰的vals被称为第一次 ,然后存储的价值。 如果你再次要求,你会得到储值。

因此,像一个模式

 def foo(x: => Expensive) = { lazy val cache = x /* do lots of stuff with cache */ } 

是最终放弃工作尽可能长久的模式。 如果你的代码path永远不会让你需要x ,那么它永远不会被评估。 如果您需要多次,则只会评估一次并存储以备将来使用。 所以你做这个昂贵的电话要么是零(如果可能的话),要么是一个(如果没有)的时间,保证。

Scala的维基百科文章甚至回答了lazy关键字的作用:

使用关键字lazy会推迟一个值的初始化,直到使用这个值。

另外,你在这个代码样例中使用q : => Parser[U]是一个按名称调用的参数。 以这种方式声明的参数仍然是未评估的,除非您在方法中的某处明确地评估它。

下面是scala REPL中关于call-by-name参数如何工作的一个例子:

 scala> def f(p: => Int, eval : Boolean) = if (eval) println(p) f: (p: => Int, eval: Boolean)Unit scala> f(3, true) 3 scala> f(3/0, false) scala> f(3/0, true) java.lang.ArithmeticException: / by zero at $anonfun$1.apply$mcI$sp(<console>:9) ... 

正如你所看到的,3/ 3/0在第二次调用中根本没有得到评估。 将懒惰的值与像上面这样的名字叫做参数结合起来的含义如下:当调用方法时不会立即计算参数q 。 相反,它被分配到懒惰值p ,这也是不立即评估。 只有后来,当使用p才会导致对q的评估。 但是,由于p是一个val所以参数q只会被计算一次 ,并且结果被存储在p以便以后在循环中重用。

你可以很容易地看到在repl,多重评估可能会发生,否则:

 scala> def g(p: => Int) = println(p + p) g: (p: => Int)Unit scala> def calc = { println("evaluating") ; 10 } calc: Int scala> g(calc) evaluating evaluating 20