目前还不清楚的是,在惰性评估和其他好处(如果有的话)方面,名称参数优于匿名函数的优势是什么:
def func1(a: => Int)
def func2(a: () => Int)
Run Code Online (Sandbox Code Playgroud)
什么时候应该使用第一个和第二个?
这不是=>,()=>和Unit =>之间差异的副本
在这两种情况下,懒惰都是一样的,但存在细微差别.考虑:
def generateInt(): Int = { ... }
def byFunc(a: () => Int) { ... }
def byName(a: => Int) { ... }
// you can pass method without
// generating additional anonymous function
byFunc(generateInt)
// but both of the below are the same
// i.e. additional anonymous function is generated
byName(generateInt)
byName(() => generateInt())
Run Code Online (Sandbox Code Playgroud)
然而,具有call-by-name的函数对于制作DSL非常有用.例如:
def measured(block: ? Unit): Long = {
val startTime = System.currentTimeMillis()
block
System.currentTimeMillis() - startTime
}
Long timeTaken = measured {
// any code here you like to measure
// written just as there were no "measured" around
}
Run Code Online (Sandbox Code Playgroud)
def func1(a: => Int) {
val b = a // b is of type Int, and it`s value is the result of evaluation of a
}
def func2(a: () => Int) {
val b = a // b is of type Function0 (is a reference to function a)
}
Run Code Online (Sandbox Code Playgroud)
一个例子可能会非常彻底地了解这些差异。
假设您想while
在 Scala 中编写您自己的真实循环版本。我知道,我知道……while
在 Scala 中使用?但这与函数式编程无关,这是一个很好地演示该主题的示例。所以和我在一起。我们将调用我们自己的版本whyle
。此外,我们希望在不使用 Scala 的 builtin 的情况下实现它while
。为了实现这一点,我们可以使我们的whyle
构造递归。此外,我们将添加@tailrec
注释以确保我们的实现可以用作内置while
. 这是第一次尝试:
@scala.annotation.tailrec
def whyle(predicate: () => Boolean)(block: () => Unit): Unit = {
if (predicate()) {
block()
whyle(predicate)(block)
}
}
Run Code Online (Sandbox Code Playgroud)
让我们看看这是如何工作的。我们可以将参数化的代码块传递给whyle
. 第一个是predicate
参数化函数。第二个是block
参数化函数。我们将如何使用它?
我们想要的是让我们的最终用户whyle
像使用while
控制结构一样使用:
// Using the vanilla 'while'
var i = 0
while(i < args.length) {
println(args(i))
i += 1
}
Run Code Online (Sandbox Code Playgroud)
但是由于我们的代码块是参数化的,我们whyle
循环的最终用户必须添加一些难看的语法糖来让它工作:
// Ouch, our whyle is hideous
var i = 0
whyle( () => i < args.length) { () =>
println(args(i))
i += 1
}
Run Code Online (Sandbox Code Playgroud)
所以。看来,如果我们希望最终用户能够以whyle
更熟悉、更原生的风格调用我们的循环,我们将需要使用无参数函数。但是接下来我们遇到了一个非常大的问题。一旦你使用无参数函数,你就不能再吃蛋糕了。你只能吃你的蛋糕。看:
@scala.annotation.tailrec
def whyle(predicate: => Boolean)(block: => Unit): Unit = {
if (predicate) {
block
whyle(predicate)(block) // !!! THIS DOESN'T WORK LIKE YOU THINK !!!
}
}
Run Code Online (Sandbox Code Playgroud)
哇。现在用户可以whyle
按照他们期望的方式调用我们的循环……但是我们的实现没有任何意义。您无法同时调用无参数函数并将函数本身作为值传递。你只能调用它。这就是我所说的只吃你的蛋糕的意思。你也不能拥有。因此,我们的递归实现现在已经消失了。它只适用于参数化函数,不幸的是它非常丑陋。
在这一点上,我们可能会想作弊。我们可以重写我们的whyle
循环以使用 Scala 的内置while
:
def whyle(pred: => Boolean)(block: => Unit): Unit = while(pred)(block)
Run Code Online (Sandbox Code Playgroud)
现在我们可以使用whyle
完全 like 了while
,因为我们只需要能够吃到我们的蛋糕……我们也不需要拥有它。
var i = 0
whyle(i < args.length) {
println(args(i))
i += 1
}
Run Code Online (Sandbox Code Playgroud)
但是我们被骗了!实际上,这是一种拥有我们自己的尾部优化版本的 while 循环的方法:
def whyle(predicate: => Boolean)(block: => Unit): Unit = {
@tailrec
def whyle_internal(predicate2: () => Boolean)(block2: () => Unit): Unit = {
if (predicate2()) {
block2()
whyle_internal(predicate2)(block2)
}
}
whyle_internal(predicate _)(block _)
}
Run Code Online (Sandbox Code Playgroud)
你能弄清楚我们刚刚做了什么吗??我们在这里的内部函数中有我们原始的(但丑陋的)参数化函数。我们用一个函数包装它,该函数将无参数函数作为参数。然后它调用内部函数并将无参数函数转换为参数化函数(通过将它们转换为部分应用函数)。
让我们尝试一下,看看它是否有效:
var i = 0
whyle(i < args.length) {
println(args(i))
i += 1
}
Run Code Online (Sandbox Code Playgroud)
确实如此!
值得庆幸的是,由于在 Scala 中我们有闭包,我们可以很长时间地清理它:
def whyle(predicate: => Boolean)(block: => Unit): Unit = {
@tailrec
def whyle_internal: Unit = {
if (predicate) {
block
whyle_internal
}
}
whyle_internal
}
Run Code Online (Sandbox Code Playgroud)
凉爽的。无论如何,这些是无参数函数和参数化函数之间的一些非常大的区别。我希望这能给你一些想法!
如果您想按Int
名称作为参数传递,则应使用第一个函数定义。如果您希望参数是返回Int
.