函数加里化(Currying)和偏函数应用(Partial Application)的比较

【名词解释】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

函数加里化(Currying)明显解决的是一个完全不同的问题:如果我们有几个单参数函数,并且这是一种支持一等函数(first-class)的语言,如何去实现一个多参数函数?函数加里化是一种实现多参数函数的方法。

下面是一个单参数的Javascript函数:

var foo = function(a) {

  return a * a;
}

如果我们受限只能写单参数函数,可以像下面这样模拟出一个多参数函数:

var foo = function(a) {

  return function(b) {
    return a * a + b * b;

  }
}

通过这样调用它:(foo(3))(4),或直接 foo(3)(4)

注意,函数加里化提供了一种非常自然的方式来实现某些偏函数应用。如果你希望函数foo的第一个参数值被固定成5,你需要做的就是var foo5 = foo(5)。这就OK了。函数foo5就是foo函数的偏函数。注意,尽管如此,我们没有很简单的方法对foo函数的第二个参数偏函数化(除非先偏函数化第一个参数)。

当然,Javascript是支持多参数函数的:

var bar = function(a, b) {

  return a * a + b * b;

}

我们定义的bar函数并不是一个加里化的函数。调用bar(5)并不会返回一个可以输入12的函数。我们只能像bar(5,12)这样调用这个函数。

在一些其它语言里,比如 Haskell 和 OCaml,所有的多参数函数都是通过加里化实现的。

下面是一个把上面的foo函数用OCaml语言写成的例子:

let foo = fun a ->

  fun b ->
    a * a + b * b

下面是把上面的bar函数用OCaml语言写成的例子:

let bar = fun a b ->

  a * a + b * b

头一个函数我们叫做“显式加里化”,第二个叫做“隐式加里化”。

跟Javascript不一样,在OCaml语言里,foo函数和bar函数是完全一样的。我们用完全一样的方式调用它们。

# foo 3 4;;
- : int = 25
# bar 3 4;;
- : int = 25

两个函数都能够通过提供一个参数值来创造一个偏函数:

# let foo5 = foo 5;;
val foo5 : int -> int = <fun>

# let bar5 = bar 5;;
val bar5 : int -> int = <fun>
# foo5 12;;
- : int = 169
# bar5 12;;
- : int = 169

事实上,我们可以把下面这个匿名函数:

fun arg1 arg2 ... argN -> exp

当作是下面这个函数的简写:

fun arg1 -> fun arg2 -> ... -> fun argN -> exp

函数加里化和偏函数应用的总结

  • 偏函数应用是找一个函数,固定其中的几个参数值,从而得到一个新的函数。
  • 函数加里化是一种使用匿名单参数函数来实现多参数函数的方法。
  • 函数加里化能够让你轻松的实现某些偏函数应用。
  • 有些语言(例如 Haskell, OCaml)所有的多参函数都是在内部通过函数加里化实现的。
[英文原文:Currying vs. Partial Application ]

分享这篇文章:

18 Responses to 函数加里化(Currying)和偏函数应用(Partial Application)的比较

  1. 抓狂 says:

    “柯里化”

  2. Null says:

    干脆用”咖喱化”

    curry
    n. 咖哩粉, 咖哩饭菜
    vt. 用咖哩粉调味

  3. Adam says:

    不知道作者是根据什么翻译成“加里化“的,求解

    • aqee says:

      哈斯凯尔·加里(Haskell Curry),维基百科上的

      • Rox Dorentus says:

        教科书(数学、计算机)里面一般是把这个姓翻译成『柯里』的

        维基百科也还有『柯里化』这个条目:http://zh.wikipedia.org/wiki/柯里化

        • aqee says:

          维基百科也还有『柯里化』这个条目中提到的人名是‘加里’,你提到的教科书是什么?书本比较权威

          • Rox Dorentus says:

            我的第一反应是觉得类似《数理逻辑》的书里面提到过他的,不过仔细回想,又不那么确定了。。也有可能因为是我后来在别的网站或者书里面见到的大都是『柯里化』才导致这个印象的吧(Haskell Curry 这个名字我反而没那么深的印象)。

            Google 了一下,『加里化』的结果比『柯里化』多,不过排除掉你这篇文章的影响之后(搜索『”加里化” -“偏函数”』vs.『”柯里化” -“偏函数”』),前者的结果数量几乎可以忽略(52000 vs. 174),那么差不多可以得出结论:在简体汉语互联网中,作为对『currying』的译名,『柯里化』更常见。

            不过说真的,Haskell Curry 被译为『哈斯凯尔·加里』倒也很普遍,看来大家都没去协调统一人名和相关术语的译名啊。。。

  4. 三桂 says:

    不知道Curry应用场景(比如javascript中的Curry)是什么?

  5. 徐风子 says:

    好怀念javascript里的mootools框架,那里面实现 偏函数,lamda表达式多轻松呀。 这么好的框架居然被淘汰了,好人不长命呀!

  6. 爆款名城 says:

    学校里没学好,现在学好逛IT博客

  7. 乔千皓 says:

    个人觉得您将图文编排设计反了,感觉文章的配图要比文字所占空间大,建议将图片适当地缩小,文字字体适当地放大些!这样可能在网站首页上的显示效果更好,注意力更高!

  8. haitao says:

    好像就是 支持缺省参数 的机制吧。。。。
    说得这么生僻。
    缺省参数对于重构而尽量不影响原有代码,非常有用!

  9. 依云 says:

    为什么非要把「partial application」译成「偏函数应用」呢?因为「偏导数」的关系吗?我觉得直接译成「部分应用」或者「函数的部分应用」更好,浅显易懂。

  10. 不懂请不要bb says:

    连柯里化都没有解释清楚就来误导别人,哎

  11. 天蓝云扬 对这篇文章的反应是赞一个

发表评论

电子邮件地址不会被公开。 必填项已用*标注