函数加里化和偏函数应用的比较

开发 后端 开发工具
Currying:因为是美国数理逻辑学家哈斯凯尔·加里(Haskell Curry)发明了这种函数使用技巧,所以这样用法就以他的名字命名为Currying,中文翻译为“加里化”。

函数式编程

 

【名词解释】Currying:因为是美国数理逻辑学家哈斯凯尔·加里(Haskell Curry)发明了这种函数使用技巧,所以这样用法就以他的名字命名为Currying,中文翻译为“加里化”。

我感觉很多人都对函数加里化(Currying)和偏函数应用(Partial Application)之间的区别搞不清楚,尤其是在相似的上下文环境中它们同时出现的时候。

偏函数解决这样的问题:如果我们有函数是多个参数的,我们希望能固定其中某几个参数的值。

几乎所有编程语言中都有非常明显的偏函数应用。在C语言中:

  1. int foo(int a, int b, int c) {  
  2.  
  3.   return a + b + c;  
  4. }  
  5.  
  6. int foo23(int a, int c) {  
  7.   return foo(a, 23, c);  
  8.  
 

foo23函数实际上就是一个foo函数的偏函数应用,参数b的值被固定为23。

当然,像这样明显的偏函数并没有太大的用处;我们通常会希望编程语言能提供我们某些偏函数特征。

例如,在Python语言中,我们可以这样做:

  1. from functools import partial  
  2.  
  3. def foo(a,b,c):  
  4.  
  5.   return a + b + c  
  6.  
  7. foo23 = partial(foo, b=23)  
  8.  
  9. foo23(a = 1, c = 3)  # => 27 
 

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

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

 
  1. var foo = function(a) {  
  2.  
  3.   return a * a;  

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

 
  1. var foo = function(a) {  
  2.  
  3.   return function(b) {  
  4.     return a * a + b * b;  
  5.  
  6.   }  

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

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

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

 
  1. var bar = function(a, b) {  
  2.  
  3.   return a * a + b * b;  
  4.  

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

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

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

 
  1. let foo = fun a ->  
  2.  
  3.   fun b ->  
  4.     a * a + b * b 

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

 
  1. let bar = fun a b ->  
  2.  
  3.   a * a + b * b 

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

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

  1. # foo 3 4;;  
  2. - : int = 25 
  3. # bar 3 4;;  
  4. - : int = 25 
 

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

 
  1. # let foo5 = foo 5;;  
  2. val foo5 : int -> int = <fun>  
  3.  
  4. # let bar5 = bar 5;;  
  5. val bar5 : int -> int = <fun>  
  6. # foo5 12;;  
  7. - : int = 169 
  8. # bar5 12;;  
  9. - : int = 169 

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

 

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

 

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

 

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

 

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

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

英文原文:Currying vs. Partial Application

译文连接:http://www.aqee.net/currying-partial-application/

责任编辑:林师授 来源: 外刊IT评论
相关推荐

2009-07-22 07:42:00

Scala偏应用函数

2010-11-18 15:46:41

Oracle Deco

2017-03-07 15:13:28

Scala偏函数函数

2010-09-07 08:35:40

DB2常用函数

2010-03-11 11:10:14

Python函数式

2020-09-23 16:07:52

JavaScript函数柯里化

2010-09-09 13:43:48

SQL函数NEWID

2009-02-24 15:39:27

字符串比较函数函数

2010-11-25 11:01:33

MySQL日期函数

2023-05-06 07:27:47

2010-09-09 09:31:19

SQL函数left

2010-07-20 09:52:27

Perl构造函数

2010-10-25 17:33:35

Oracle数学函数

2013-10-11 13:01:45

LinuxLinux Shell

2009-07-22 07:57:00

ScalaCurry化函数

2010-04-30 11:41:39

Oracle常用函数

2010-07-19 08:45:45

SQL Server

2009-02-24 16:17:41

日期时间函数

2010-09-08 15:24:28

TCP IP协议栈

2010-11-03 16:25:51

DB2列函数
点赞
收藏

51CTO技术栈公众号