scala中的延迟函数定义

dav*_*k01 14 design-patterns scala lazy-loading lazy-evaluation lazy-initialization

我一直在学习scala,我得说它是一种非常酷的语言.我特别喜欢它的模式匹配功能和函数文字,但我来自javascript,ruby背景,这些语言中我最喜欢的模式之一是惰性函数和方法定义模式.javascript中的一个例子是

var foo = function() {
  var t = new Date();
  foo = function() {
    return t;
  };
  return foo();
};
Run Code Online (Sandbox Code Playgroud)

带有小调整的相同代码在ruby中工作,您只需使用单例对象在执行计算后重新定义该方法.当涉及昂贵的计算时,这种事情非常方便,如果您需要结果,则提前不知道.我知道在scala中我可以使用缓存来模拟相同类型的结果,但我正在尝试避免条件检查,到目前为止,我的实验已经返回了负面结果.有没有人知道scala中是否存在惰性函数或方法定义模式?

注意:javascript代码来自Peter Michaux的网站.

Rex*_*err 30

JavaScript中所有复杂的代码似乎只是尝试缓存日期的值.在Scala中,您可以轻松地实现同样的目标:

lazy val foo = new Date
Run Code Online (Sandbox Code Playgroud)

并且,如果甚至不想制作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
Run Code Online (Sandbox Code Playgroud)

模式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!")
  }
}
Run Code Online (Sandbox Code Playgroud)

现在只有在需要时才会生成(通过by-name参数)存储它并在需要时重新使用它(通过lazy val).

所以这样做,而不是JavaScript方式,即使你可以让Scala看起来很像JavaScript.


Ran*_*ulz 19

Scala有lazy vals,除非使用val,否则不对其初始化程序进行求值.延迟val可以用作方法局部变量.

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
}
Run Code Online (Sandbox Code Playgroud)

在此方法中,用作实际参数的表达式将被评估为零次(如果方法体的动态执行路径从不使用ii)或一次(如果它使用ii一次或多次).

  • @ davidk01 - 但是你不想以JavaScript的方式去做,即使你可以.在Scala中有更清晰的方法来实现同样的功能! (2认同)

Mr_*_*Qqn 10

你可以定义一个lazy val这是一个函数:

lazy val foo = {
  val d = new Date
  () => { d }
}

println(foo())
Run Code Online (Sandbox Code Playgroud)

foo() 现在将每次返回相同的Date对象,第一次调用foo时将初始化的对象.

为了解释一下代码,第一次调用foo()时{ val d = new Date; () => { d } },d被赋值给一个新的日期值,然后它计算最后一个表达式() => { d }并将其赋值给foo值.然后foo是一个没有参数返回d的函数.


Mil*_*bin 6

我认为一些响应者对你提出这个问题的方式感到有些困惑.你想要的Scala构造是一个简单的惰性定义:

lazy val foo = new java.util.Date
Run Code Online (Sandbox Code Playgroud)

Date对象的构造最多只发生一次,并推迟到第一次引用foo.