柯里化与偏函数
Currying: 因为是美国数理逻辑学家哈斯凯尔·柯里(Haskell Curry)发明了这种函数使用技巧, 所以这样用法就以他的名字命名为 Currying, 中文翻译为 “柯里化”.
我感觉很多人都对函数柯里化(Currying)和偏函数应用(Partial Application)之间的区别搞不清楚, 尤其是在相似的上下文环境中它们同时出现的时候.
偏函数应用
偏函数应用解决这样的问题: 如果我们有函数是多个参数的, 我们希望能固定其中某几个参数的值.
几乎所有编程语言中都有非常明显的偏函数应用. 在 C 语言中:
int foo(int a, int b, int c) { return a + b + c; } int foo23(int a, int c) { return foo(a, 23, c); }
foo23
函数实际上就是一个
foo
函数的偏函数应用, 参数 b 的值被固定为 23.
当然, 像这样明显的偏函数并没有太大的用处; 我们通常会希望编程语言能提供我们某些偏函数特征, 例如, 在 Python 语言中, 我们可以这样做:
from functools import partial def foo(a, b, c): return a + b + c foo23 = partial(foo, b=23) foo23(a = 1, c = 3) # => 27
函数柯里化
函数柯里化解决的是一个完全不同的问题: 如果我们有几个单参数函数, 并且这是一种支持一等函数(first-class)的语言, 如何去实现一个多参数函数? 柯里化是一种实现多参数函数的方法.
下面是一个单参数的 Javascript 函数:
var foo = function(a) { return a * a; }
如果我们受限只能写单参数函数 (在最初, 柯里化也确实是为了在单一参数的 lambda 演算中研究多参函数), 可以像下面这样模拟出一个多参数函数:
var foo = function(a) { return function(b) { return a * a + b * b; } }
通过这样调用它: foo(3)(4)
.
注意, 函数柯里化提供了一种非常自然的方式来实现某些偏函数应用.
如果你希望函数 foo
的第一个参数值被固定成 5,
你需要做的就是 var foo5 = foo(5)
. 这就 OK 了,
函数 foo5
就是 foo
函数的偏函数.
当然, Javascript 本身就是支持多参数函数的, 但在一些其它语言里, 比如 Haskell 和 OCaml, 所有的多参数函数都是隐式通过柯里化实现的.
总结
- 当要固定的是函数前面的参数, 可用柯里化.
- 当要固定的是函数结尾的参数, 可用偏函数.
- 偏函数应用是固定多参函数中某些个参数值, 从而得到一个新的函数.
- 函数柯里化是一种使用单参数函数来实现多参数函数的方法.
- 函数柯里化能够让你轻松的实现某些偏函数应用.
- 有些语言(例如 Haskell, OCaml)所有的多参函数都是在内部通过函数柯里化实现的.