柯里化与偏函数

Published Thu 06 September 2018 in 函数式

by HanXiao  


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)所有的多参函数都是在内部通过函数柯里化实现的.

原文传送门