lambda创世纪

lambda创世纪

lambda介绍

λ演算(英语:lambda calculus,λ-calculus)是一应用于研究函数定义、函数应用和递归的形式系统。它由阿隆佐·邱奇和他的学生斯蒂芬·科尔·克莱尼在20世纪30年代引入。邱奇运用λ演算在1936年给出判定性问题(Entscheidungsproblem)的一个否定的答案。这种演算可以用来清晰地定义什么是一个可计算函数。关于两个lambda演算表达式是否等价的命题无法通过一个“通用的算法”来解决,这是不可判定性能够证明的头一个问题,甚至还在停机问题之先。Lambda演算对函数式编程语言有巨大的影响,比如Lisp语言、ML语言和Haskell语言。

Lambda演算可以被称为最小的通用程序设计语言。它包括一条变换规则(变量替换)和一条函数定义方式,Lambda演算之通用在于,任何一个可计算函数都能用这种形式来表达和求值。因而,它是等价于图灵机的。尽管如此,Lambda演算强调的是变换规则的运用,而非实现它们的具体机器。可以认为这是一种更接近软件而非硬件的方式。

关于这个lambda演算,我们理解上面两句话就行了(其实我只看懂这两句):

  • λ演算(英语:lambda calculus,λ-calculus)是一应用于研究函数定义、函数应用和递归的形式系统。
  • 它是等价于图灵机的。

第一句话很好理解,在编程方面来说就是匿名函数,是一种以很简单简洁的方法来表达一个函数,而且不用起名字。
第二句话就是这篇文章的目的所在,我们来看看为何lambda演算是图灵等价的,这里不是证明它而是展示它。

用lambda创造世界

不管怎么说,我们知道图灵机lambda演算 解决了同一个问题,后来又被论两者是完全等价的。那么lambda怎么创造世界呢?在lambda的世界中又应该有些什么呢?前面已经说过lambda就是一个匿名函数,而lambda的世界只有匿名函数。

先来看一个例子,我希望实现这样的功能是,对于一个列表[1,2,3,4,...n],若其中的数字为偶数则返回true,奇数则返回false。用erlang代码可表示为:

is_even(List)->
    lists:map(fun(Number)-> (Number rem 2) =:= 0 end, List).

在lambda的世界里全是函数,根本没有数字类型,取余操作,判断相等,列表这些高级的东西。所以我们需要从头开始创建。

lambda是一个函数,所谓函数就是一个动作,那么怎么用一个动作表示一个数字呢?我们这里使用一个动作执行多少次来表达数字,例如1这个动作执行1次,这个动作执行2次表示2.

Zero = fun(Fun, X)-> X end,
One = fun(Fun, X)-> Fun(X) end,
Two = fun(Fun, X)-> Fun(Fun(X)) end,
Three = fun(Fun, X)-> Fun(Fun(X)) end,

用了数字,就有应用在数字上的操作比如1+1=2, 2-1=1等等。我们先来创造最简单也是最基本的加1,减1操作:

%%加1,返回一个函数,这个函数接受两个参数,返回比X多一次动作的调用。
Increment = fun(X)-> fun(Fun, Y)-> Fun(X(Fun,Y)) end end,

 %%调试函数,输出这个数字
PrintInteger = fun(X)-> X(fun(Y)-> Y+1 end, 0) end,

 举个例子如果X是One那么调用PrintInteger(One)返回结果展开为:
(fun(Fun, X)-> Fun(X) end)(fun(Y)->Y+1 end, 0)
=> 再展开
(fun(Y)-> Y+1 end)(0)
=> 再展开
1+0
=>
 1

以上,我们定义了加1操作,并能打印出lambda世界的数字为图灵世界的数字。但是减1有点麻烦了。由于是一个函数表示一个数字,而我们的数字表示的意思是执行动作多少次,然而我们并不能取出函数内部的东西,所以对于减少一次动作也无从说起,但是呢我们有一个笨办法来绕过这个困难,我们可以有一个结构[a,a+1],然后不断的推进这个a,当a+1 = n的时候,a就是n-1了。用erlang来描述这个推进可以表示为:

slide([A, B])->
    [B, B+1].

那么我们只要对0执行n次slice,再取列表的左边的值就能得到n-1了。但是我们得先实现包含两个的元素的结构。

%有序对
Pair = fun(X, Y)-> fun(F)-> F(X,Y) end end,
%取左边的值
Left = fun(P)-> P(fun(X,Y)-> X end) end,
%取右边的值
Right = fun(P)-> P(fun(X,Y)-> Y end) end

%滑动
Slide = fun(P)-> Pair(Right(P), Increment(Right(P)))

现在我可以来实现减1操作了

%减1
Decrement = fun(X)-> Left(X(Slide(Pair(Zero,Zero))) end
%可以看到我设置的最小数为0.

现在加1,减1都有,我们可以创建更多的数字操作了:

%加法
Add = fun(X, Y)-> X(Increment, X) end,
%减法
Substract = fun(X, Y)-> Y(Decrement, X) end,
%乘法
Multiply = fun(X, Y)-> Y(fun(X2)-> X2+X end, Zero) end

现在有了数字操作,我们得解决判断是否是偶数的情况了。A rem 2 =:=0,首先得实现bool类型然后是取余操作,然后是实现等于操作。bool类型的功能主要是从两个状态中选择一个,所以可以像下面这样实现:

%boolean类型
True = fun(X, Y)-> X end,
False = fun(X, Y)-> Y end,

%判断是否为0
IsZero = fun(X)-> X(fun(Y)-> True end, False) end,

%实现取余有点麻烦,我们按照下面这个算法实现
%%erlang版本
mod(X, Y)->
    if
        Y =< X ->
            mod(X - Y, Y);
        true ->
            X
    end.

%但是在这个版本中我们使用了if操作,所以得先实现if
If = fun(Bool, X, Y)-> Bool(X, Y)  end,

%现在来实现mod操作,但是有个小问题,在mod中出现了递归,但是在匿名函数我们不能递归啊,因为此时函数并没有传给一个变量,所以我们把函数自己也当变量传入。
%实现mod操作之前,我们还得实现 =< 操作
% X =< Y => Y-X =< 0 但是我们这里最小的数是0,不管怎么减都是0,所以相当于 Y-X = 0 .
IsLessEq =  fun(X, Y)-> IsZero(Substract(Y, X)) end,

TpMod = fun(X, Y, Self)-> If(IsLessEq(Y, X), Self(Substract(X, Y), Y, Self), X) end,
%但是这里If中还是有个问题,由于传参时,会对参数理解调用,所以Self(Substract(X, Y), Y)会被立即执行,所以要想办法让它延长执行,我们通过封装一下把它变成一个函数,而不是调用的形式来达到这一点。
%改写TpMod
TpMod = fun(X, Y, Self)-> If(IsLessEq(Y, X), fun()-> Self(Substract(X, Y) end, Y, Self), fun()-> X end) end,
%但是这个封装的函数什么时候调用呢???所以得修改下True跟false
True = fun(X, Y)-> X() end,
False = fun(X, Y)-> Y() end

%我们把TpMod也封装下,使之变成两个参数
Mod = fun(X, Y)-> TpMod(X, Y, TpMod) end,

好了,我们已经实现mod操作了,已经完成大部分内容了,接下来我们要实现列表,用以存放数据,但是如何实现列表呢?你已经猜到了,没错,就是利用前面的有序对。比如erlang中的列表[1, 2, 3] => 有序对中是这样的 [1, [2, 3]] 但是这样不能得到列表是否为空,所以我们指定有序对左边的值表示列表是否为空。
[1, [2, 3]] => [false, [1, [false, [2, [false, [3, [true, true]]]]]]].
接下来实现它:

Empty = Pair(True, True),
IsEmpty = fun(L) -> Left(L) end,
Unshift = fun(L, X)-> Pair(False, Pair(X, L)) end,
First = fun(L)-> Left(Right(L)) end,
Rest = fun(L)-> Right(Right(L)) end,

%列表有了,现在我们实现如何产生一个范围
TpRange = fun(X, Y, Self)->
            UnShift(If(IsLessEq(X, Y), fun()-> Self(Increment(X), Y, Self) end, fun()-> Empty end), X)
          end,
Range = fun(X, Y)-> TpRange(X, Y, TpRange) end,

%我们还需要实现map操作,实现如下:

TpMap = fun(L, Fun, Self)->
          If(IsEmpty(L), fun()-> Empty end, fun()-> UnShift(Self(Rest(L), Fun, Self), Fun(First(L))) end)
        end,
Map = fun(L, Fun) -> TpMap(L, Fun, TpMap) end,

%现在我们已完成全部准备工作了,下面是对列表进行判断是否偶数的算法

L = Range(One, Multiply(Three, Three)), %% 1 - 9
Re = Map(L, fun(X)-> IsZero(Mod(X, Two)) end),

%为了判断我们是否得到正确的结果,我们把结果输出出来。
PrintBool = fun(Bool)-> Bool(fun()->true end, fun()-> false end) end,
%把结果打印出来
io:format("["),
Map(Re, fun(X)-> io:format("~p, ", [PrintBool(X)]), end)
io:format("]").

=> 结果为:
[true, false, true, false, true, false, true, false, true, false, ]ok

(完)

posted on 2015-12-20 10:46  德不孤  阅读(559)  评论(0编辑  收藏  举报

导航