考虑以下功能:
import java.util.concurrent.Callable;
def callable[T]( operation: =>T) : Callable[T] = {
new Callable[T] {
def call : T = operation
}
}
在REPL中,此代码执行我想要的操作:
scala> val myCallable = callable {
| println("Side effect!");
| "Hi!"
| }
myCallable: java.util.concurrent.Callable[String] = $anon$1@11ba4552
scala> myCallable.call
Side effect!
res3: String = Hi!
scala> myCallable.call
Side effect!
res4: String = Hi!
在调用函数“call”之前,不会计算by-name参数,并且每次调用该函数时都会重新评估该参数。这就是我想要的行为。
但在spec中,它说明了以下有关名字参数:
“相应的参数不会在函数应用程序中进行评估,而是在函数内的每次使用时进行评估。”
从这个描述中,我不清楚我可以依赖我想要的行为。 “在功能中使用意味着什么”是什么意思?我如何知道这是指我的Callable被调用的点(在无限期的某个时间),而不是它定义的点(非常“在函数内”)?
代码正在做我想要的。但是如果我确定这种行为是可靠的,那么我会更容易休息,而不是在scala的某个未来版本中可能会“修复”的错误。
答案 0 :(得分:2)
这不是错误 - 行为符合预期。您可以将“在函数内的每次使用时评估”递归地视为“在评估该表达式时在函数中的表达式中的每次使用时进行评估”。
答案 1 :(得分:2)
“函数”是您将参数传递给的函数。这篇文章试图警告你的是:
scala> def byName(arg: => String) = arg + arg
byName: (arg: => String)java.lang.String
scala> byName({println("hi") ; "foo" })
hi
hi
res0: java.lang.String = foofoo
即。每次引用参数时都会发生副作用。由于你只做了一次,这与你的情况无关(除了评估点,它在函数内部,而不是在调用站点)。
答案 2 :(得分:1)
要扩展先前的答案并阐明避免这种情况的方法,如果您只希望对其进行一次求值,则可以在函数内的val中捕获值。通过执行此操作,您将导致评估“按名称”参数,并且多次使用计算值而不是对同一表达式进行2次评估。
scala> def byName(arg: => String) = {val computedArg = arg; computedArg + computedArg}
byName: (arg: => String)java.lang.String
scala> byName({"println("hi") ; "foo" })
hi
res0: java.lang.String = foofoo
如果您将来需要这样做......
答案 3 :(得分:0)
最后,要获得方法参数的真正 lazy 评估(仅限零个或一个评估),您可以这样做:
def m1(i: => Int) = {
lazy val li = i
...
// any number of static or dynamic references to li
...
}