scala中的懒惰函数定义
我一直在学习scala,我得说这是一种非常酷的语言。我特别喜欢它的模式匹配功能和函数文字,但是我来自javascript,ruby背景,并且在这些语言中我最喜欢的模式之一是懒惰函数和方法定义模式。在JavaScript的一个例子是scala中的懒惰函数定义
var foo = function() {
var t = new Date();
foo = function() {
return t;
};
return foo();
};
相同的代码稍作调整工作在红宝石,你只需要使用单独的对象重新定义法进行计算后。当涉及昂贵的计算时,这种事情非常方便,而且如果您需要结果,您不会提前知道。我知道在scala中我可以使用缓存来模拟同样的结果,但我试图避免条件检查,到目前为止,我的实验已经返回负面结果。有谁知道在scala中是否有懒惰的函数或方法定义模式?
注意:JavaScript代码来自Peter Michaux的site。
JavaScript中所有复杂的代码似乎只是试图缓存日期的值。在Scala中,可以实现平凡同样的事情:
lazy val foo = new Date
而且,如果甚至不希望做一个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
,该模式expensive: => String
被称为由名称参数,你能想到的是,“给我的东西,将产生上要求的字符串。”请注意,如果你使用它的两倍,它在每次的时间,这就是兰德尔·舒尔茨的方便模式来在重新生成:
def maybeExpensiveTwice(doIt: Boolean, expensive: => String) {
lazy val e = expensive
if (doIt) {
println(e)
println("Wow, that was " + e.length + " characters long!")
}
}
现在你只生成如果你需要它(通过按姓名参数)和存储它,并重新使用它,如果你再次需要它(通过懒惰val)。
所以这样做,而不是JavaScript的方式,即使你可能使Scala看起来很像JavaScript。
我只是想弄清楚在scala中实现相同结果的习惯用法,就像你指出的那样,懒惰的vals和名字参数是scala中的方法。 – davidk01 2010-08-26 07:21:45
最后一部分似乎并不正确。我测试了第二部分,如果您传递doIt参数为true,那么每次都会调用昂贵的函数。你可以通过传递这个函数来测试它def expensive = {println(“I Get Called”); “return value”}然后用(1到3)测试它.foreach(_ => maybeExpensiveTwice(true,expensive)) – Reza 2013-12-08 14:16:22
@Reza - 我的意思是它只被调用一次_per方法invocation_尽管在该方法中使用了两次。当然,如果您多次调用该方法,则会在调用该方法时多次调用该方法。 (为了避免你需要将它缓存在更高的层次上。) – 2013-12-15 21:28:04
斯卡拉有lazy val
s,其初始值不被评估,除非和直到val被使用。惰性vals可以用作方法局部变量。
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
一次或多次) 。
感谢您提供关于名称参数和惰性val的信息,我相信他们会在简化我的一些代码时派上用场。至于我原来的问题,事实证明,斯卡拉允许类似JavaScript重新定义的函数变量,所以几乎相同的代码工作。我所有的原始测试都有语法错误,导致我无法获得与javascript中相同的结果。 – davidk01 2010-08-25 04:23:32
@ davidk01 - 但即使可以,你也不想用JavaScript的方式来做。在Scala中有更多更干净的方法来实现同样的事情! – 2010-08-25 14:37:20
当使用这种技术时,有没有一种方法可以明确地清除方法外部的val?我问自己什么时候val会被GC清理。一般的GC行为是清楚的,但是使用技术可能会让人困惑,因为这种方法可以作为函数传递并存储。我处于一种情况,这种技术非常有用,但是在执行完一个特定的点后,我想清理阀值,但我不想清理周围的物体。任何方式来实现这一点,或者这是不可能的,因为它是一个val? – user573215 2013-10-14 13:35:04
我认为你的意思是“懒功能”是函数文字或匿名函数。
在斯卡拉你可以做这样的事情,非常类似于你发布的JavaScript代码。
val foo =() => {
val t = new Date()
val foo =() => {t}
foo()
}
println ("Hello World:" + foo())
的主要区别在于:
- 你不能重新分配外富
- 没有“功能”关键字,而不是你使用类似(S:字符串)= > {code}
- 最后一条语句是块的返回值,所以你不需要添加“return”。
您可以定义一个懒惰的VAL这是一种功能:
lazy val foo = {
val d = new Date
() => { d }
}
println(foo())
foo()
现在每次返回相同的Date对象,这将是首次初始化FOO被称为对象。
为了解释一下代码,第一次foo()被调用{ val d = new Date;() => { d } }
被执行,d被赋值给一个新的日期值,然后它评估最后一个表达式() => { d }
并将其赋值给foo值。那么foo是一个没有返回d的参数的函数。
我不知道什么关于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>
如果你想要得到的功能,你可以把它的功能类型的子类型:
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
我认为一些响应者对你说的这个问题的方式有些困惑。斯卡拉构建你想在这里是一个简单的懒人定义:
lazy val foo = new java.util.Date
Date对象的建设将至多出现一次被推迟,直到第一次引用FOO。
请记住,除非真的没有回答你的问题,否则你应该给答案一个漂亮的绿色复选标记! – 2010-08-25 17:38:21
+1链接到Peter Michaux网站上非常有趣的文章。 :-) – 2010-12-01 08:18:09