• 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
    

    (完)

  • 相关阅读:
    做成像的你不得不了解的真相9-相机制冷温度越低越好么?
    做成像的你不得不了解的真相8-如影随形的噪声(下)
    做成像的你不得不了解的真相8-如影随形的噪声(中)
    做成像的你不能不了解的真相8-如影随形的噪声(上)
    做成像的你不能不了解的真相7-两分钟测算相机增益(Gain)
    做成像的你不能不了解的真相6-分辨率(2)
    做成像的你不能不了解的真相6-分辨率(1)
    做成像的你不能不了解的真相5-图像信噪比计算
    做成像的你不能不了解的真相4-灰度值与电子数
    做成像的你不能不了解的真相3-信噪比2
  • 原文地址:https://www.cnblogs.com/quitboy/p/5060366.html
Copyright © 2020-2023  润新知