美文网首页
在时间上分离的可计算函数的递归性质具有元语言的抽象能力

在时间上分离的可计算函数的递归性质具有元语言的抽象能力

作者: 可乐杯杯hh | 来源:发表于2020-09-24 11:14 被阅读0次

    在上一篇文章中,我用lambda实现了一个快速排序的算法,这个算法的实现和大部分利用索引来实现的算法不同,它没有使用变量的赋值和修改,相反的是,它只有纯函数式的逻辑流程,而且从本质上来说,我们的语言构建块,已经改变了,加号有了一个新的含义,它可以在时间上完全分离,生成一个全新的版本,而不会对现有的系统产生破坏性的作用,由于我们的语言是基于消息和中缀式的数学定义,那么它的使用,看起来和本身系统内部的原生函数,并没有太大的不同,我们的系统的可分解性,达到了语言级别,每一个消息的参数响应对象,都是在上下文中约定的,我们可以再添加无穷多个递归的消息栈,它们都可以利用同一个符号来表达不同的含义。

    在smalltalk中,这个语法被实现为左结合,实际上还有更初始的方法,就是使用数学符号的运算符结合,这个思想大致可以在sasl,miranda,erlang,prolog和haskell中找到,由于alan kay受到lisp的影响比较大,所以采用了这样的定义,我们的新函数式语言,希望通过重新给数学符号添加定义,从而给这个新的逻辑工具带来不一样的样子,我们人思考问题,也是具有上下文的,这些上下文,最终把动态的规则构成了一个静态的形象,比如说直角坐标系,函数图像来表现一个物体的状态,等等等等,一个函数就是一个全局的状态参量,它精确描述了一个系统每一个瞬间的状态,使用它们之间复杂的递归关系来对整个世界进行建模。

    我们的系统就是在lambda函数的基础上,添加这样一个分离式的时态逻辑的语法糖,它每一个新的表达式,就是给系统增加新的规则,一个规则定义了一个概念在某个上下文的特定含义,这个形式我们使用三种方法来描述,第一种是scheme版本,(lambda (X) E),第二种是miranda版本,f(X) = E,第三种是smalltalk版本,f X1 = E1, f X2 = E2...,需要注意的是,smalltalk版本虽然很好,但是它可能在某些地方不太简单,所以我们使用miranda版本来作为一个替代,miranda版本看起来更像我们普通人写一个函数该有的样式,然后我们的基础库的表现形式也是smalltalk样式,但是这些基础操作符和对象,它们都是纯函数式的,也就是说,它们用起来虽然是smalltalk的语法,但是它不具备可修改性,它们可以在递归过程中返回一个新的自身对象,也就是self这个对象,它是在时间上解耦了的,所以不会出现持续递归的现象,这种对系统的抽象导致它看起来也具有状态,然而它自己已经在时间中消失了,取代它的是它的一个新的对象,这个对象也不具备状态,但是它可以被用来生成新的对象。

    我们先来看第一个程序,平方的写法:

    //定义
    square := f(x) = (x * x);
    //使用
    square 5;
    = 25
    

    第二个程序,阶乘:

    //第一种写法
    fac := f 1 = 1, f(n) = n * (fac (n - 1));
    //第二种写法
    fac := f(n) = n == 1
     true: 1
     false: n * (fac (n-1));
    //第三种写法
    fac 1 = 1;
    fac (n) = n * (fac (n - 1));
    

    因为f(x) = E的形式可以写为一个匿名函数,所以我们可以这样:

    (f(x) =  (x * x)) 5
    = 25
    

    因为列表实际上是一种闭包对象,但是我们专门用一个符号来代表列表,这个符号就是[],[1 2 3] = [1. 2. 3. [] ],这是一个链表的结构。
    然后我们可以支持模式匹配和列表解析,也就是说,列表可以提前解析

    h.t = [1.2.3.[]];
    h
    =1
    t
    =[2.3.[]]
    

    然后我们可以给列表的第一个元素添加标签,从而给系统添加新的匹配规则:

    vec.h1.t1 * vec.h2.t2 = (h1 * h2) + (t1 * t2) ;
    vec.h1.[] * vec.h2.t2 = 'err' ;
    vec.h1.t1 * vec.h2.[] = string.[$e $r $r];
    vec.[] * vec.[] = 0 ;
    

    我们把第二个err提示写为string.[$e $r $r],那么大家大概能理解内部的一些实现,上面这几条规则,定义了向量的乘法。
    然后我们可以写一个新的大于规则,列表的大于规则。

    h.t > x = h > x true: [h] false: [] + t > x;
    [] > x = [];
    

    这个规则匹配起来会是这样

    [1 2 3 4 5 6] > 2 ;
    = [3 4 5 6] 
    

    然后我们可以使用宏定义,实际上宏定义并不特殊,它就是一个规则生成模板。

    gen-compare compare := 
    h.t compare x = h compare x true: [h] false: [] + t compare x,
    [] compare x = [];
    

    这样我们就可以定义其他的运算符

    gen-compare <=;
    gen-compare <;
    gen-compare >=;
    

    有了这些之后,我们还想要什么呢,我们可能想知道,list + list这个是什么意思,也就是说

    [1 2 3 4] + [2 3 4 5]
    = ?
    

    也许我们的List可能是集合,也可能是向量,还有可能是字符,还可能是矩阵元素,这些都无所谓,我们只需要加标签就可以,但是我们现在可以定义哪些运算呢,我们可以有集合,有向量,有这个列表。

    //列表的加法
    [h.t] + [x] = [h] + (t + [x])
    [h] + [x] = [h x]
    //向量的加法
    vec.[h1.t1] + vec.[h2.t2] = vec.([(h1 + h2)] + (t1 + t2))
    vec.[] + vec.[] = vec.[]
    

    有了这些构建工具之后,我们可以写一些程序了,比如说快速排序

    h.t qst = (h <= t qst) + [h] + (h > t qst)
    []  qst = []
    

    矩阵乘法

    //定义一个矩阵,它的行是r,列是c,数据是d
    mk-mx r c d :=
      f r = r ,
      f c = c ,
      f(r: c:) = (d ((r: * c) + c:));
    //这里之所以可以这么写是因为list的实现内部可以利用索引直接作为参数获取它的值
    //list本身是一个函数,具体实现可以参考上一篇文章的my-list对象。
    
    //定义一个方法,取得矩阵的某列
    c-r mx c-i =
      (c-l i = i == mx r
        true: []
        false: [(mx r:i c:c-i)] + (c-l (i + 1)))
      c-l 0;
    //定义一个方法,取得某行
    r-f mx r-i =
      (r-l i = c-i == mx c
        true: []
        false: [(mx r:r-i col:i)] + (r-l (i + 1)))
      r-l 0;
    //这是乘法,它从A,B矩阵中得到另一个新的矩阵
    mx.A * mx.B = (A c) == (B r)
       (true: 
           f r = A r ,
           f c = B r ,
           f(r: c:) = vec.(r-f A r:) * vec.(c-f B c:))
       false: 'err';
    

    可以看出来,这个时间分离的递归方法完全用函数的思维去对数据进行建模,因为所有数据都是函数,那么它的上下文可以无限的精简,以至于写出来的每一个程序就是一门语言,它的意义是上下文相关的,所以具有极端的伸缩性。

    相关文章

      网友评论

          本文标题:在时间上分离的可计算函数的递归性质具有元语言的抽象能力

          本文链接:https://www.haomeiwen.com/subject/ivlzyktx.html