Lambda演算与科里化(Currying)

Lambda演算

早在现代计算机问世以前,Lambda演算(λ演算)已经由图灵的老师阿隆佐·邱奇(Alonzo Church)引入。这种演算可以用来清晰地定义什么是一个可计算函数。它包括一条变换规则(变量替换)和一条函数定义方式,Lambda 演算的通用性在于任何一个可计算函数都能用这种形式来表达和求值。因而,它等价于后来提出的图灵机。Lambda 演算对函数式编程语言有巨大的影响,比如 Lisp 语言、ML 语言和 Haskell 语言(后面还会提到Haskell这个人)。

简介

在Lambda演算中的每一个表达式都代表一个单输入(单参数)的函数,函数的输出(值)仍然是一个这样的函数。例如一个简单的增值函数 f(x) = x + 1在Lambda演算中我们可以这样表示:λx. x + 1。这里的x叫做这个Lambda表达式的参数。相应的f(2)可以表示为:(λx. x + 1)2。特别的这里的参数也可以是一个类似的函数,在Lambda演算中所有函数都是匿名函数,它们既可以是其他函数的返回值也可以是参数,下面来看一个稍微复杂一点的例子: (λ f. f 3)(λ x. x+1) 。这个表达式的意思是说要将f 3这个行为应用到我们先前定义的函数λ x. x+1上去。该表达式等价于 (λ x. x + 2) 3 = 3+2。

Lambda演算的形式化定义

Lambda演算的过程是由若干个Lambda表达式构成的。一个Lambda表达式包含了一组变量v1, v2, …, vn,以及抽象符号‘λ’和‘( )’。这些Lambda表达式的集合Λ递归地定义如下:

如果 x 是一个变量, 则x ∈ Λ

如果 x 是一个变量且 M ∈ Λ, 则 (λx.M) ∈ Λ

如果M, N ∈ Λ, 则(M N) ∈ Λ

科里化(Currying)

科里化的概念最早由俄国数学家Moses Schönfinkel引入,而后由著名的数理逻辑学家哈斯格尔·科里(Haskell Curry)将其丰富和发展,Currying由此得名。它表示一种将一个带有N元组参数的函数转换成N个一元函数链的方法。前面提到的Lambda演算使用的都是1元函数,在有两个或者多个变量的情况下就要使用科里化了。

Currying的过程

Currying如何进行的呢?不妨来看一个直观的例子, 假设有如下函数:f(x, y, z) = x / y +z. 要求f(4,2, 1)的值。

首先,用4替换f(x, y, z)中的x,得到新的函数g(y, z) = f(4, y, z) = 4 / y + z

然后,用2替换g(y, z)中的参数y,得到h(z) = g(2, z) = 4/2 + z

最后,用1替换掉h(z)中的z,得到h(1) = g(2, 1) = f(4, 2, 1) = 4/2 + 1 = 3

很显然,如果是一个n元函数求值,这样的替换会发生n次,注意,这里的每次替换都是顺序发生的,这和我们在做数学时上直接将4,2,1带入x / y + z求解不一样。在这个顺序执行的替换过程中,每一步代入一个参数,每一步都有新的一元函数诞生,最后形成一个嵌套的一元函数链。这些一元函数和之前提到的Lambda演算中的单输入的函数是一回事。于是,通过Currying,我们可以对任何一个多元函数进行化简,使之能够进行Lambda演算。用C#来演绎上述Currying的例子就是:

           Func<int,Func<int, Func<int, int>>> func = x => y =>z=> x/y + z;
           Console.WriteLine(func(4)(2)(1)); //显示3

偏函数应用

Currying的概念将函数式编程的概念和可变参数结合在一起,一个带n 个参数,科里化的函数固化第一个参数为固定参数,并返回另一个带n-1 个参数函数对象。这有点像我们求一个多元函数的偏导数的过程,这种函数将任意数量(顺序)的参数的函数转化成另一个带剩余参数的函数对象。这种行为也有个类似的名字---偏函数应用(Partial Function Applications)。

这里以Python 3.0中的PFA为例,要用到偏函数功能需要先导入functools模块。

>>> from functools import partial
>>> def f(x,y):
         return x+y

>>> p = partial(f,1) #固定一个参数1
>>> p(3)
4
>>> p(300)
301

当然,还可以让一个函数接收另一个函数作为参数,比如下面的求和函数:

>>> def justSum(func,L):
         return sum([func(item) for item in L])

这样就可以用它来接收我们定义的任意函数,并对它们在L上的值求和了。比方说我们可以通过这样的方式来求平方和:

>>> sqrsSum = partial(justSum,lambda x:x*x)
>>> sqrsSum([1,2,3,4,5])
55

C#中也有类似地实现,不过受委托强类型的限制,比较难看:-(,(见阅读资料[3])

阅读资料 

[1] http://en.wikipedia.org/wiki/Lambda_calculus

[2] http://en.wikipedia.org/wiki/Currying

[3] http://blogs.msdn.com/wesdyer/archive/2007/01/29/currying-and-partial-function-application.aspx

黄季冬

2009年10月22日

Lambda演算与科里化(Currying)相关推荐

  1. JS中的柯里化(currying) 转载自张鑫旭-鑫空间-鑫生活[http://www.zhangxinxu.com]

    JS中的柯里化(currying) by zhangxinxu from http://www.zhangxinxu.com 本文地址:http://www.zhangxinxu.com/wordpr ...

  2. java函数式编程-科里化

    java函数式编程-科里化 什么是函数? 在数学上,函数的定义为"它接受零个或多个参数,生成一个或多个结果" 而在java8中,函数的定义为像数学函数一样没有副作用的函数 什么是副 ...

  3. java8函数式编程笔记-科里化

    java函数式编程-科里化 什么是函数? 在数学上,函数的定义为"它接受零个或多个参数,生成一个或多个结果" 而在java8中,函数的定义为像数学函数一样没有副作用的函数 复制代码 ...

  4. java 科里化_关于柯里化(curry)

    在实际使用的过程中对柯里化有了一些无法从书本上直接获得的感受. 在JAVA中函数不是头等公民,必须通过接口进行外观统一以后,才能通过实例作为载体进行处理逻辑的传递, 最容易理解的例子莫过于Strate ...

  5. 【逻辑与计算理论】Lambda 演算——开篇

    原文来自Good Math/Bad Math的系列连载,全文分7章,本篇是第1章.中文博客负暄琐话对这个系列的前6章做过翻译,强迫症表示忍受不了「下面没有了」,于是自己动手做了全套.这里只对原文做了翻 ...

  6. 温故而知新:柯里化 与 bind() 的认知

    什么是柯里化? 科里化是把一个多参数函数转化为一个嵌套的一元函数的过程.(简单的说就是将函数的参数,变为多次入参) const curry = (fn, ...args) => fn.lengt ...

  7. JavaScript 专题之函数柯里化

    JavaScript 专题系列第十三篇,讲解函数柯里化以及如何实现一个 curry 函数 定义 维基百科中对柯里化 (Currying) 的定义为: In mathematics and comput ...

  8. [转载] 纯函数和函数柯里化

    参考链接: 用示例编写Java柯里化Currying函数 文章目录 纯函数什么是纯函数纯函数例子非纯函数例子 函数柯里化函数柯里化简单例子参数复用 纯函数 什么是纯函数 如果函数的调用参数相同,则永远 ...

  9. Scala 高阶函数(作为值的函数、匿名函数、闭包、柯里化)+隐式转换和隐式参数...

    Scala高级特性 1.    学习目标 1.1.   目标一:深入理解高阶函数 1.2.   目标二:深入理解隐式转换 2.    高阶函数 2.1.   概念 Scala混合了面向对象和函数式的特 ...

  10. 7.scala初识 柯里化、隐式参数、隐式转换、视图边界、上界、下界、协变、逆变

    1.前言: 学过java我们都知道,java中的继承是对类的增强,java中的代理.装饰是对对象方法的增强.而在scala中,隐式转换和隐式参数是Scala中两个非常强大的功能,隐式的对类的方法进行增 ...

最新文章

  1. 对10个整数按由大到小顺序排序
  2. Python 基础:分分钟入门
  3. [翻译]XNA 3.0 Game Programming Recipes之twenty-one
  4. postgresql 排序索引
  5. 搜索引擎web spam类型及防治策略(version 0.9)
  6. NoClassDefFoundError: org/apache/flink/streaming/api/datastream/DataStream一例解决
  7. c++字符串数组去重的另类思路
  8. Android kernel源码下载与编译
  9. 音响系统测试软件苹果,再谈汽车音响调试专用相位软件JL AUDIO Tools,苹果ios坛友的福音!|汽车数码...
  10. TimePicker使用全解
  11. 人生哲理 之 驴子的故事
  12. This may be due to a lack of SYSV IPC support
  13. opencv函数cv2.warpAffine 和 cv2.warpPerspective 的理解和复现
  14. VGGNet网络结构
  15. 【转】一个老程序员的心理话
  16. 编辑器生成静态网页_不使用静态网站生成器的7个理由
  17. 解决Mysql执行删除操作报错:1093的问题
  18. Flink kafka connectors 源码详解---<1>
  19. xdf文档怎么转换为pdf_如何将PDF文件和图像转换为Google文档文档
  20. 手机记事本软件敬业签如何添加定时提醒

热门文章

  1. 北漂程序员,何以露宿街头?
  2. FFmpeg总结(十四)FFmpeg如何解析直播点播m3u8
  3. OpenGL ES总结(四)OpenGL 渲染视频画面
  4. Android Multimedia框架总结(二)MediaPlayer框架及播放网络视频案例
  5. python基础篇--从零开始(中)PyCharm、Vscode安装。
  6. 稀疏表示的合适字典_基于共振稀疏分解的滚动轴承早期微弱故障诊断
  7. java 删除zk节点_zookeeper 的节点删除不了?
  8. matlab函数isempty_MATLAB的Cell数组操作
  9. cpu(s)和%CPU的的区别
  10. python中filter是什么意思_求能人解答python filter()和filter_by()区别是什么