斯卡拉的懒惰的论点:他们是如何工作的?
在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