概述
在 “完全”函数式编程 一文中,我们初步感受了函数式编程的力量:以非常简洁的代码,通过组合任何短小的简单函数,实现更加复杂功能的复合函数。
有小伙伴可能会问: 既然函数是这么重要的原材料,那么从哪里可以获取这么多原材料呢 ?总不成,一个个去定义吧。别急,Curry 提供了批量生产函数的能力,—— 如果星云是恒星的工厂,那么,Curry 就是函数的工厂。
本文将以 Groovy 为示例,讲解 Curry 的特性及能力。
基础知识
小伙伴都学过多元函数,比如 f(x,y) = x^2 + y ,当 y = 0 时,就变成 f(x) = x^2 平方函数; 当 x = 1 时,就变成 f(y) = 1+y 线性函数。 Curry ,通俗地说,就是将多变量的函数,通过逐个因变量赋值,从而批量生成函数的能力。
如下代码所示: def closure = { x,y -> x^2 +y } 定义了一个二元函数 f(x,y) , rcurry(0) 即是将 y 赋值为 0 (右边的参数),这样就得到了一元函数 square(x) = x^2 ; curry(1) 即是将 x 赋值为 1 (默认是左边的参数),就得到了 linear(y) = 1+y 。 嗯,就是这么简单。
class Basics {
static void main(args) {
def closure = { x,y -> x * x +y }
def square = closure.rcurry(0)
println((1..5).collect { square(it) })
def linear = closure.curry(1)
println((1..5).collect { linear(it) })
}
}
输出结果:
[1, 4, 9, 16, 25]
[2, 3, 4, 5, 6]
实际应用
那么 Curry 有什么实际应用呢 ? 总不能只停留在数学的领域里。 实际上 Curry 的能力非常强大。我们来实现一个通用调用器。在实际工程中,常常需要调用具有相同参数的函数,或者对不同的对象进行相同的处理。
假设有两个订单处理函数: cancel, complete ,有一个列表排序函数 sort:
def static sort(list) {
list.sort()
return list
}
def static cancel(order) {
println ("cancel: " + order.orderNo)
}
def static complete(order) {
println ("complete: " + order.orderNo)
}
class OrderParam {
def orderNo
def shopId
}
可以定义一个二元函数 generalCaller ,可以对指定的参数对象 param 使用指定的 handler 进行处理。
def generalCaller = { param, handler -> handler(param) }
现在,来看怎么使用:
第一种方式:指定参数对象。 可以构造一个订单对象,使用 Curry ,就得到了可以处理这个订单的函数处理器 orderProcessor, 然后就可以传入不同的订单处理器进行处理。this.&method 是 Groovy 对函数的引用。
def orderProcessor = generalCaller.curry(new OrderParam(orderNo: 'E0001', shopId: 55))
orderProcessor(this.&cancel)
orderProcessor(this.&complete)
第二种方式,指定处理器。 就得到了一个对参数进行排序的函数 sort(param) , 然后传入不同的可排序对象进行处理:
def sorter = generalCaller.rcurry(this.&sort)
println sorter([4,7,2,6,1,3])
println sorter(["i", "have", "a", "dream"])
是不是非常灵活 ? 在 “函数柯里化(Currying)示例” 一文中,使用 Curry 实现了一个简易的文件处理框架。
小结
Curry 可以将高维函数逐步降维,批量生成大量的低维函数。 如果有一个 N 维函数,思考下,通过 Curry ,可以生成多少个低维函数 ?Curry 结合函数式编程,蕴藏着惊人的潜力。