您的位置:首页 > 编程语言

函数式编程里的高阶函数

2013-07-02 00:00 501 查看

高阶函数

我记得自己在了解了上面列出的种种优点后曾想:“这都很棒,可是如果我不得不用天生残缺的语言对着全是 final 的变量编程,好特性也毫无意义。” 这其实是误解。在如 Java 这般的命令式语言环境里,所有变量都是 final 将带来一堆问题,但是在函数式语言里并非如此,函数式语言提供了不同的抽象工具使你忘记曾经习惯于修改变量。高阶函数就是这样一种工具。

函数式语言中的函数不同于 Java 或 C 中的函数,而是一个超集——它有着 Java 函数拥有的所有功能,但还有更多。创建函数的方式和 C 中相似:

int add(int i, int j) {
return i + j;
}

这里有些东西和等价的 C 代码有区别。现在扩展我们的 Java 编译器使其支持这种记法:当我们输入上述代码后编译器会把它转换成下面的 Java 代码(别忘了,所有东西都是 final 的):


class add_function_t {int add(int i, int j) { return i + j; }}
add_function_t add = new add_function_t();

这里的符号 add 并不是一个函数。这是一个有一个成员函数的很小的类。

我们现在可以把 add 作为函数参数放入我们的代码中。还可以把它赋给另一个符号。我们在运行时创建的 add_function_t 的实例如果不再被使用就将会被垃圾回收掉。这些使得函数成为第一级的对象无异于整数或字符串。操作(作为参数的)函数的函数被称为高阶函数。别让这个术语吓着你,这和 Java 的 class 操作其它(作为参数的)class 没什么区别。我们本可把它们称为“高阶类”但没有人注意到这个,因为 Java 背后没有一个强大的学术社区。

那么何时以及如何使用高阶函数呢?我很高兴你这样问,如果你不曾考虑类的继承层次,就可能写出一整团堆砌的代码块。当你发现其中一些代码重复出现,就把他们提取成函数(幸运的是这些依然可以在学校里学到),如果你发现在那个函数里一些逻辑动作根据情况有变,就把他提取成高阶函数。糊涂了?下面是 一个来自我工作中的实例:假如我的一些 Java 代码接受一条信息,用多种方式处理它然后转发到其他服务器。

class MessageHandler {
void handleMessage(Message msg) {
// …
msg.setClientCode(”ABCD_123″);
// …
sendMessage(msg);
}
// …
}

假设现在要更改这个系统,我们要把信息转发到两个服务器而不是一个,一切基本都像刚才一样,但第二个服务器接受另一种客户代码(client code)格式,怎么处理这种情况?我们可以检查信息的目的地并相应修改客户端代码的格式,如下:

class MessageHandler {
void handleMessage(Message msg) {
// …
if(msg.getDestination().equals(”server1″) {
msg.setClientCode(”ABCD_123″);
} else {
msg.setClientCode(”123_ABC”);
}
// …
sendMessage(msg);
}
// …
}

然而这不是可扩展的方法,如果加入了更多的服务器,这个函数将线性增长,更新它会成为梦魇。面向对象的方法是把 MessageHandler 作为基类,在子类中定制客户代码操作:

abstract class MessageHandler {
void handleMessage(Message msg) {
// …
msg.setClientCode(getClientCode());
// …
sendMessage(msg);
}
abstract String getClientCode();
// …
}
class MessageHandlerOne extends MessageHandler {
String getClientCode() {
return “ABCD_123″;
}
}
class MessageHandlerTwo extends MessageHandler {
String getClientCode() {
return “123_ABCD”;
}
}

现在就可以对每个服务器实例化一个适合的处理类,添加服务器的操作变得容易维护了。但对于这么一个简单的修改仍然要添加大量的代码。为了支持不同的客户代码我们创建了两个新的类型!现在我们用高阶函数完成同样的功能:

class MessageHandler {
void handleMessage(Message msg, Function getClientCode) {
// …
Message msg1 = msg.setClientCode(getClientCode());
// …
sendMessage(msg1);
}
// …
}
String getClientCodeOne() {
return “ABCD_123″;
}
String getClientCodeTwo() {
return “123_ABCD”;
}
MessageHandler handler = new MessageHandler();
handler.handleMessage(someMsg, getClientCodeOne);

没有创建新的类型和新的 class 层次,只是传入合适的函数作为参数,完成了面向对象方式同样的功能,同时还有一些额外的优点。没有使自己囿于类的层次之中:可以在运行时传入函数并在任何时候以更高的粒度更少的代码修改他们。编译器高效地为我们生成了面向对象的“粘合”代码!除此之外,我们还获得了所有函数式编程的其他好处。当然函数式语言提供的抽象不只这些,高阶函数只是一个开始。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: