scala 中的惰性函数定义
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3562610/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
lazy function definitions in scala
提问by David K.
I've been learning scala and I gotta say that it's a really cool language. I especially like its pattern matching capabilities and function literals but I come from a javascript, ruby background and one of my favorite patterns in those languages is the lazy function and method definition pattern. An example in javascript is
我一直在学习 scala,我不得不说它是一种非常酷的语言。我特别喜欢它的模式匹配功能和函数文字,但我来自 javascript、ruby 背景,我最喜欢这些语言中的模式之一是惰性函数和方法定义模式。javascript中的一个例子是
var foo = function() {
var t = new Date();
foo = function() {
return t;
};
return foo();
};
The same code with minor tweaks works in ruby where you just use the singleton object to redefine the method after the computation is performed. This kind of thing comes in really handy when expensive computation are involved and you don't know ahead of time if you are going to need the result. I know that in scala I can use a cache to simulate the same kind of result but I'm trying to avoid conditional checks and so far my experiments have returned negative results. Does anyone know if there is a lazy function or method definition pattern in scala?
相同的代码在 ruby 中也有细微的调整,您只需在执行计算后使用单例对象重新定义方法。当涉及昂贵的计算并且您不知道是否需要结果时,这种事情非常方便。我知道在 Scala 中我可以使用缓存来模拟相同类型的结果,但我试图避免条件检查,到目前为止我的实验返回了负面结果。有谁知道 Scala 中是否有惰性函数或方法定义模式?
Note: The javascript code is from Peter Michaux's site.
注意:javascript 代码来自 Peter Michaux 的站点。
回答by Rex Kerr
All that complicated code in JavaScript appears to just try to cache the value of the date. In Scala, you can achieve the same thing trivially:
JavaScript 中所有复杂的代码似乎只是尝试缓存日期的值。在 Scala 中,您可以轻松实现相同的目标:
lazy val foo = new Date
And, if don't even want to make a val, but want to call a function that will only execute the expensive code if it needs it, you can
而且,如果甚至不想创建 val,而是想调用一个仅在需要时才执行昂贵代码的函数,您可以
def maybeExpensive(doIt: Boolean, expensive: => String) {
if (doIt) println(expensive)
}
maybeExpensive(false, (0 to 1000000).toString) // (0 to 1000000).toString is never called!
maybeExpensive(true, (0 to 10).toString) // It is called and used this time
where the pattern expensive: => Stringis called a by-name parameter, which you can think of as, "Give me something that will generate a string on request." Note that if you use it twice, it will regenerate it each time, which is where Randall Schultz' handy pattern comes in:
其中模式expensive: => String被称为按名称参数,您可以将其视为“给我一些将根据请求生成字符串的东西”。请注意,如果您使用它两次,它每次都会重新生成,这就是 Randall Schultz 的方便模式的用武之地:
def maybeExpensiveTwice(doIt: Boolean, expensive: => String) {
lazy val e = expensive
if (doIt) {
println(e)
println("Wow, that was " + e.length + " characters long!")
}
}
Now you generate only if you need it (via the by-name parameter) andstore it and re-use it if you need it again (via the lazy val).
现在,您仅在需要时生成(通过 by-name 参数)并存储它并在再次需要时重新使用它(通过惰性 val)。
So do it this way, not the JavaScript way, even though you couldmake Scala look a lot like the JavaScript.
所以这样做,而不是 JavaScript 的方式,即使你可以让 Scala 看起来很像 JavaScript。
回答by Randall Schulz
Scala has lazy vals, whose initializers are not evaluated unless and until the val is used. Lazy vals may be used as method local variables.
Scala 有lazy vals,除非使用 val,否则不会评估其初始值设定项。惰性 val 可用作方法局部变量。
Scala also has by-name method parameters, whose actual parameter expressions are wrapped in a thunk and that thunk is evaluated every time the formal parameter is referenced in the method body.
Scala 也有按名称的方法参数,其实际参数表达式被包装在一个 thunk 中,并且每次在方法体中引用形式参数时都会评估该 thunk。
Together these can be used to achieve lazy evaluation semantics such as are the default in Haskell (at least in my very limited understanding of Haskell).
这些可以一起用于实现惰性求值语义,例如 Haskell 中的默认值(至少在我对 Haskell 的非常有限的理解中)。
def meth(i: => Int): Something = {
// ^^^^^^ by-name parameter syntax
lazy val ii = i
// Rest of method uses ii, not i
}
In this method, the expression used as the actual parameter will be evaluated either zero times (if the dynamic execution path of the method body never uses ii) or once (if it uses iione or more times).
在此方法中,用作实际参数的表达式将被评估零次(如果方法体的动态执行路径从不使用ii)或一次(如果它使用ii一次或多次)。
回答by Mr_Qqn
You can define a lazy val which is a function :
您可以定义一个惰性 val,它是一个函数:
lazy val foo = {
val d = new Date
() => { d }
}
println(foo())
foo()will now return the same Date object each time, object which will be initialized the first time foo is called.
foo()现在每次都会返回相同的 Date 对象,该对象将在第一次调用 foo 时被初始化。
To explain the code a little, the first time foo() is called { val d = new Date; () => { d } }is executed, d is assigned to a new date value then it evaluate the last expression () => { d }and assign it to the foo value. Then foo is a function with no parameters which return d.
为了稍微解释一下代码,第一次调用 foo() 时{ val d = new Date; () => { d } },d 被分配给一个新的日期值,然后它评估最后一个表达式() => { d }并将其分配给 foo 值。那么 foo 是一个没有参数的函数,它返回 d。
回答by Miles Sabin
I think some of the responders were a little confused by the way you phrased the question. The Scala construct you want here is a simple lazy definition:
我认为有些回答者对您提出问题的方式感到有些困惑。您在这里想要的 Scala 构造是一个简单的惰性定义:
lazy val foo = new java.util.Date
The construction of the Date object will occur at most once and be deferred until the first reference to foo.
Date 对象的构造最多发生一次,并被推迟到对 foo 的第一次引用。
回答by Eastsun
I known nothing about Ruby, but scala has singleton object pattern also:
我对 Ruby 一无所知,但 Scala 也有单例对象模式:
Welcome to Scala version 2.8.0.r22634-b20100728020027 (Java HotSpot(TM) Client VM, Java 1.6.0_20).
Type in expressions to have them evaluated.
Type :help for more information.
scala> object LazyInit {
| val msec = { println("Hi,I'm here!"); System.currentTimeMillis }
| }
defined module LazyInit
scala> System.currentTimeMillis
res0: Long = 1282728315918
scala> println(System.currentTimeMillis +" : " + LazyInit.msec)
Hi,I'm here!
1282728319929 : 1282728319930
scala> println(System.currentTimeMillis +" : " + LazyInit.msec)
1282728322936 : 1282728319930
scala> println(System.currentTimeMillis +" : " + LazyInit.msec)
1282728324490 : 1282728319930
scala>
If you want to get function ,you can make it subtype of a function type:
如果要获取 function ,可以将其设为 function 类型的子类型:
scala> object LazyFun extends (() => Long) {
| val msec = System.currentTimeMillis
| def apply() = msec
| }
defined module LazyFun
scala> System.currentTimeMillis
res2: Long = 1282729169918
scala> println(System.currentTimeMillis + " : " + LazyFun())
1282729190384 : 1282729190384
scala> println(System.currentTimeMillis + " : " + LazyFun())
1282729192972 : 1282729190384
scala> println(System.currentTimeMillis + " : " + LazyFun())
1282729195346 : 1282729190384
回答by Brian Hsu
I think what you mean "lazy function" is function literal or anonymous function.
我认为您所说的“懒惰函数”是函数字面量或匿名函数。
In Scala you could do things like this, very similar to the javascript code you posted.
在 Scala 中,您可以执行这样的操作,与您发布的 javascript 代码非常相似。
val foo = () => {
val t = new Date()
val foo = () => {t}
foo()
}
println ("Hello World:" + foo())
The main difference is that:
主要区别在于:
- You could not re-assignment the outer foo
- There is no "function" keyword, instead you use something like (s:String) => {code}
- The last statement is the return value of a block, so you don't need add "return".
- 您无法重新分配外部 foo
- 没有“function”关键字,而是使用类似 (s:String) => {code}
- 最后一个语句是块的返回值,所以不需要加“return”。

