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

+1

请记住,除非真的没有回答你的问题,否则你应该给答案一个漂亮的绿色复选标记! – 2010-08-25 17:38:21

+0

+1链接到Peter Michaux网站上非常有趣的文章。 :-) – 2010-12-01 08:18:09

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。

+1

我只是想弄清楚在scala中实现相同结果的习惯用法,就像你指出的那样,懒惰的vals和名字参数是scala中的方法。 – davidk01 2010-08-26 07:21:45

+0

最后一部分似乎并不正确。我测试了第二部分,如果您传递doIt参数为true,那么每次都会调用昂贵的函数。你可以通过传递这个函数来测试它def expensive = {println(“I Get Called”); “return value”}然后用(1到3)测试它.foreach(_ => maybeExpensiveTwice(true,expensive)) – Reza 2013-12-08 14:16:22

+0

@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一次或多次) 。

+0

感谢您提供关于名称参数和惰性val的信息,我相信他们会在简化我的一些代码时派上用场。至于我原来的问题,事实证明,斯卡拉允许类似JavaScript重新定义的函数变量,所以几乎相同的代码工作。我所有的原始测试都有语法错误,导致我无法获得与javascript中相同的结果。 – davidk01 2010-08-25 04:23:32

+2

@ davidk01 - 但即使可以,你也不想用JavaScript的方式来做。在Scala中有更多更干净的方法来实现同样的事情! – 2010-08-25 14:37:20

+0

当使用这种技术时,有没有一种方法可以明确地清除方法外部的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。