Scala中的懒惰功能定义
-
01-10-2019 - |
题
我一直在学习Scala,我必须说这是一种非常酷的语言。我特别喜欢它的模式匹配功能和功能文字,但我来自JavaScript,Ruby背景和这些语言中我最喜欢的模式之一是懒惰的函数和方法定义模式。 JavaScript中的一个例子是
var foo = function() {
var t = new Date();
foo = function() {
return t;
};
return foo();
};
在Ruby中,带有次要调整的相同代码可在其中使用Singleton对象在执行计算后重新定义该方法。当涉及昂贵的计算时,这种事情真的很方便,如果您需要结果,您将不知道。我知道,在Scala中,我可以使用缓存来模拟相同的结果,但是我试图避免有条件的检查,到目前为止,我的实验返回了负面结果。有人知道Scala中是否有懒惰的功能或方法定义模式?
注意:JavaScript代码来自Peter Michaux的 地点.
解决方案
JavaScript中所有复杂的代码似乎只是尝试缓存日期的值。在Scala中,您可以琐碎地实现相同的事情:
lazy val foo = new Date
而且,如果甚至不想制作阀
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
图案的位置 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!")
}
}
现在,您仅在需要时生成(通过byname参数) 和 将其存储并重复使用,如果您再次需要(通过Lazy Val)。
因此,即使您 可以 使Scala看起来很像JavaScript。
其他提示
Scala有 lazy val
s,除非和直到使用阀门,否则其初始化器不会被评估。懒阀可以用作方法局部变量。
Scala还具有副方法参数,其实际参数表达式包裹在thunk中,并且每次在方法主体中引用形式参数时都会评估thunk。
这些共同可以用来实现懒惰的评估语义,例如Haskell中的默认语义(至少在我对Haskell非常有限的情况下)。
def meth(i: => Int): Something = {
// ^^^^^^ by-name parameter syntax
lazy val ii = i
// Rest of method uses ii, not i
}
在此方法中,将评估用作实际参数的表达式零次(如果方法的动态执行路径永远不会使用 ii
)或一次(如果使用 ii
一次或多次)。
您可以定义一个懒惰的阀,这是一个函数:
lazy val foo = {
val d = new Date
() => { d }
}
println(foo())
foo()
现在,每次都将返回相同的日期对象,第一次调用FOO时将初始化的对象。
为了稍微解释代码,第一次称为foo() { val d = new Date; () => { d } }
被执行,D分配给新的日期值,然后评估最后一个表达式 () => { d }
并将其分配给FOO值。然后,foo是一个函数,没有返回d的参数。
我认为一些响应者对您的问题的方式有些困惑。您想要的Scala构造是一个简单的懒惰定义:
lazy val foo = new java.util.Date
日期对象的构建最多一次一次,并将其推迟到首次引用FOO。
我对Ruby一无所知,但是Scala也有Singleton对象模式:
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>
如果要获得函数,则可以使其成为功能类型的子类型:
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
我认为您的意思是“懒惰函数”是函数字面或匿名函数。
在Scala中,您可以做这样的事情,与您发布的JavaScript代码非常相似。
val foo = () => {
val t = new Date()
val foo = () => {t}
foo()
}
println ("Hello World:" + foo())
主要区别是:
- 您无法重新分配外部foo
- 没有“函数”关键字,而是使用(s:string)=> {code}之类的东西
- 最后一个语句是块的返回值,因此您不需要添加“返回”。