这篇博客介绍一下Scala中的partial application,局部应用,或者叫做柯里化。
所谓柯里化就是指把一个接受多个参数的函数的一部分参数写死,剩下的一部分由调用者提供。
用Java代码来表述,大概可以写成这样:
1 2 3 4 5 6 7 8 9 10 11
| public String greet(String greeting, String name) { return greeting + " " + name; }
public String sayHello(String name) { return greet("Hello", name); }
public String greetXiaoMing(String greeting) { return greet(greeting, "Xiao Ming"); }
|
greet用来给某个不确定的人打个不确定的招呼。
sayHello用来给某个不确定的人说一句固定的Hello。
greetXiaoMing用来给一个固定的人小明打一个不确定的招呼。
如果用Scala来表达同样的含义的话,可以这样写:
1 2 3 4 5
| def greet(greeting: String, name: String) = greeting + " " + name
def sayHello = greet("hello", _: String)
def greetXiaoMing = greet(_: String, "Xiao Ming")
|
其实比Java代码也简单不了多少。只是把暂时不确定的参数用下划线指代出来。
然后我们就可以在稍后需要调用它们的时候再把参数传入:
1 2
| sayHello("world") greetXiaoMing("Ni Hao")
|
然后我们看一下这个语言特性是怎么实现的呢?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| public String greet(String greeting, String name) { return new StringBuilder().append(greeting).append(" ").append(name).toString(); }
public Function1<String, String> sayHello() { return new AbstractFunction1() { public static final long serialVersionUID = 0L;
public final String apply(String x$1) { return Hello.this.greet("hello", x$1); } }; }
public Function1<String, String> greetXiaoMing() { return new AbstractFunction1() { public static final long serialVersionUID = 0L;
public final String apply(String x$2) { return Hello.this.greet(x$2, "Xiao Ming"); } }; }
|
可以看到sayHello和greetXiaoMing并不是返回String的,它们返回的是Function1 of String, String。也就是说直接调用它们俩是得不到我们想要的结果的,必须把这个Function1上的apply再调一下才行。实际上正是如此,这段代码:
1 2
| sayHello("world") greetXiaoMing("Ni Hao")
|
会被编译成:
1 2
| sayHello().apply("world"); greetXiaoMing().apply("Ni Hao");
|
除此之外,partial application还可以有另一种稍微另类一些的写法:
1 2 3 4 5
| def greet(greeting: String)(name: String) = greeting + " " + name
def sayHello = greet("hello")(_)
def greetXiaoMing = greet(_: String)("Xiao Ming")
|
反编译的结果和上面的代码是完全一致的。
我不太清楚这种写法存在的意义是不是仅仅起一个向外界宣称“我这个函数之所以出现就是给你局部应用的,不要一下子把两个参数都给我”的作用。