【发布时间】:2011-09-11 22:24:48
【问题描述】:
有人知道scala中=>单元的类型吗?我不知道 => Unit 的含义以及如何使用它。我定义了如下函数:
def test(code: => Unit){
print("start ...")
code
print("end ....")
}
test(print(1))
这是否意味着任何参数返回 Unit 的函数?
谢谢
【问题讨论】:
有人知道scala中=>单元的类型吗?我不知道 => Unit 的含义以及如何使用它。我定义了如下函数:
def test(code: => Unit){
print("start ...")
code
print("end ....")
}
test(print(1))
这是否意味着任何参数返回 Unit 的函数?
谢谢
【问题讨论】:
这种参数被称为按名称参数
=> B 代表一个代码块,它返回一个B 值,它们的目的是只有在你调用参数时才会计算它们。
def foo(code: => Int) {
println("Not yet evaluated")
val result = code
println("parameter evaluated %s, is it an int ? %s " format (
result, result.isInstanceOf[Int]) )
}
您可以通过以下方式拨打foo:
foo(1)
或
val a = 3
val b = 5
foo {
val c = a * a
c * b
}
另一种传递参数的方式是按值:参数在发送到方法之前被评估
def foo(code : Int) {
println("Parameter already evaluated")
val result = code
println("parameter evaluated : " + result)
}
Extract from the Book Functionnal Programming in Scala
More differences between by-name parameter and by-value parameter illustrated
【讨论】:
foo { ... }。
在x: => Type 中,x 是一个按名称调用 参数。这与采用不带参数的函数的参数不同:x: () => Type
【讨论】:
test(print(1))而不是test(() => print(1)))。
这称为by name 参数,与按名称调用参数评估策略有关。请参阅链接的维基百科文章,了解类似但不相同的参数传递方式。
为了更好的解释,让我们首先考虑两种最常见的参数评估策略:按值调用和按引用调用。
按值调用是迄今为止最常见的评估策略。例如,它是 Java 中唯一的策略,也是 C 中的默认策略。例如,考虑这个简单的 Java 程序:
public class ByValue {
static public void inc(int y) {
y++;
}
static public void main(String... args) {
int x = 0;
inc(x);
System.out.println(x);
}
}
它将打印0,因为x 的值被复制到y,所以当y 增加时它不会改变x 中的原始值。将此与此 C++ 程序与按引用调用的程序进行对比:
#include <stdio.h>
void inc(int &y) {
y++;
}
int main() {
int x = 0;
inc(x);
printf("%d\n", x);
}
这将打印1,因为对x的引用被传递给inc,而不是x的值。
请注意,Java 按值传递对象引用,这导致一些人声称它确实按引用调用。这是不正确的,如果您将 new 对象分配给函数的参数,它不会反映在函数的调用者中。
那么,按名称调用是什么样的?在按名称调用中,既不传递值也不传递引用。相反,整个代码被传递,并且在任何使用参数的地方,代码都被执行并使用它的结果。例如:
object ByName {
def incIfZero(y: => Int): Int = if (y == 0) y + 1 else y
def main(args: Array[String]) {
var x = 0
x = incIfZero( { val tmp = x; x += 1; tmp } )
println(x)
}
}
这个例子打印2而不是1,因为作为参数传递的代码块被计算了两次。执行的时候好像main的第二行是这样写的:
x = if ({ val tmp = x; x += 1; tmp }) { val tmp = x; x += 1; tmp } + 1 else { val tmp = x; x += 1; tmp }
现在,按名称参数至少有三个有趣的用途:
我认为,第一个和最后一个案例非常明显。这是第二种情况的示例:
implicit def fromBoolean(b: Boolean) = new {
def and(that: => Boolean): Boolean = if (b) that else b }
val x = 0
(x > 0) and (10 / x > 0)
如果that 不是按名称参数,则会在最后一行抛出异常。事实上,它只会返回false。
【讨论】:
它的意思是call-by-name,这基本上意味着在你的函数中使用时计算该值。与 Java(以及具有常规类型语法的 Scala)中默认的按值调用相反,在调用方法之前计算值。我猜,单位类型在按值调用中没有多大意义..
这通常用于创建充当自定义控制结构的函数,或者像您的示例中那样的计时或日志记录。在 Java 中你经常使用 AOP 做的事情。
【讨论】: