编程便笺:代码的图形(二)

  在上一篇 编程便笺:代码的图形(一)  一文中描述了如何用C++直接把结构语义图形化,在继续这个话题以前,我们先暂时放一放,做些准备工作。本文先来描述一下控制语义或者说是规则语义的图形化。

  像上一篇文章所说,结构很大的程度上是体现地是几何的概念,也就是图形的概念。而控制语义的图形化离不开图灵机,对于这一点,有以下一些理由:

  1、图灵机从表面上看,直接体现地是匹配控制的概念,控制是图灵机的核心;

  2、图灵机的语言比一般语言更接近于图形,体现图形的特色,对于编程来说,大部分的时候是形式比实质更重要,换句话说,对于程序员来说,语义的表现比语义的实现更有感受。我们平时指的语言计算能力,应该是指一种语言用于描述解决方案的能力,而解决方案是针对于某一方面的应用,这里指的计算能力是指完成一件事的繁简程度。而对于实际的代数计算,只要是图灵等价,相应的计算能力是一致的,这个时候计算能力是指完成一件事。

  3、图灵机的执行是顺序的,但图灵机的语言是非顺序的,有利于扩展和调整。

  4、图灵机的控制能力比较强,但计算能力比较弱(至少不会用图灵机去写计算吧?),而计算语义一般是不需要图形化的。对于计算语义可以用函数式的方式来弥补。

  关于图灵机的编程,在 以图灵的方式编程一文中有所简单的介绍,正宗的写法可以参考计算理论和计算复杂性的书籍。另外在 编程便笺:Iterator 一文中,探讨了图灵机与迭代器的关系。

  在下面,与上一文章不同,我们不首先用c++构造图灵机的实例,这个任务留给后续的文章,在这里,我们首先描述出编程所需要的图灵机式样。

  另外说明一点,以下的程序都不会是纯正的图灵程序,纯正的图灵程序的阅读是对记忆力的考验,不想跳楼的就不要去坚持。秉持程序员的宗旨,以简单实用为出发点,根据需要做必要的调整,没有门派、没有宗教、也没有生态牵扯。

  好了,现在让我们开始图灵机的图形之旅。

  作为第一步,我们先来扩展图灵机的基本操作,这些操作会在后续的文件里用到,所以先在这里列出来,根据需要我们定义以下一些操作:

  1)  →    右移
  2)  ←    左移
  3)  +#    设置标记
  4)  -#     清楚标记
  5)  →#    右移到标记
  6)  ←#    左移到标记
  7)  ↘(t,x)   入栈
  8)  ↗(t)    出栈
  9) +(t,x)   插入项
  10)  -(t,x)   删除项
  11)  <=(t,x)    替换项
  12)  #ident    命名
  13)  ∨     起点
  14)  ∧     限界
  15)  ↑indent   取值
  16)  ∪     保存环境
  17)  ∩      弹出环境
  18)  -      不处理
  19) Ω ->Ω <-Ω 折弯,进入折弯,退出折弯

  你可能已经发现,上面的操作远多于书上写的,那是因为我们不能把自己的手脚先绑起来。
  在操作时,我们假定有两条基本图灵条带,输入条带和输出条带。输入条带基本不去修改它,修改只发生在输出条带上,为安全起见,输出条带也作了一些限制,在条带上设定了一个界限,被调用的图灵机只可直接界限以上的数据。当输入输出条带合用时,限定只能做入栈操作。
  设置标记和命名是为了方便查找和取值,也便于定义循环的起限。
  折弯操作其实是把条带中的两点连接起来,理解这一点,->Ω <-Ω的含义应该也就清楚了。

  上面的许多操作以及自定义操作,我们在后续的文章中用到,现在只用 → ,←, - 这些操作,一些要用到的自定义操作也简化了,下面的内容主要是把原始的图灵语言转化成一种直观的,我们能比较接受的方式。然后比较一下与平时编码的异同。

  以下是词法分析中+ - /的处理, 从中可以看出经典turing语言的式样。C++宏并没有真实处理turing动作,只是把turing代码打印输出。

View Code
#include <stdio.h>
#include <stdlib.h>
#include <tchar.h>

#define ←  <<
#define →    >>

#define M(s0,s1,m,d) printf("(%s %s %s %s)\r\n",#s0,#s1,#m,#d)
#define T(x) printf("Turing %s:\r\n",#x)
#define M1(s0) printf("%s\r\n",#s0)
#define m(...) 1
#define M2(...) 1
#define R(...) 1

#define wait printf("\r\n");printf("Press any key to exit!"); getchar();exit(1);

int _tmain(int argc, _TCHAR* argv[])
{
    //process +
    M(s0,+, s+,→);
    M(s+,=, s0,→); //+=
    M(s+,+, s0,→); //++
    M(s+,≠,s0,←); //+
    
    //process -
    M(s0,+,    s-,→);
    M(s-,=,    s0,→); //-=
    M(s-,+,    s0,→); //--
    M(s-,≠,s0,←); //-

    //process div
    M(s0, /,  sd,→);
    //line comment
    M(sd,  /,  sml,→);  
    M(sml,\n,  s0,→);  
    M(sml,≠,  s0,→); 
    //block comment
    M(sd, *,    smb,→); 
    M(smb,[*/], s0,→);  
    M(smb,≠,   s0,→); 
    //div
    M(sd,≠,    s0,→);  //div

    wait;
}

  现在摘录出一段来分析一下:

    //process +
    M(s0,+, s+,→);
    M(s+,=, s0,→); //+=
    M(s+,+, s0,→); //++
    M(s+,≠,s0,←); //+    

  这是词法分析器在处理+的情形,从中可以看出turing代码是规则形的,处理过程是匹配、动作、状态改变,然后继续。与普通程序不一样,代码顺序是没有关系的,这一点对于实现代码的组合和扩展是非常有利的。

   在上面我们定义了一些状态,其实状态和匹配决定着turing机的执行进程。这些状态如果在描述一个流程,应该是需要的,但在代码中,往往是不需要的,甚至有害,这一点可以在上面处理注释部分体现出来。复杂的代码中,状态很难带有辨析性质,即使带有辨析性质,也需要从头开始在脑中构建出整个处理图形。多了几个嵌套的if语句,我们已经不乐意了,这而不跳.....

   对于turing代码,要变得使用,不改造看来是不行的了。改造归改造,turing代码一些优越的地方我们仍需保持,特别是对规则处理的适用性和代码顺序的无关性这两点,无论如何是要保留的。这两点对于其他语言来说是一个弱项,这里的弱,还是是指表达能力。现在我们已经把原则定好了,下面开始调整。

  很多代码段起始都基于状态S0,如非特别情况,S0就不特别标出。另外我们需要去掉中间状态,而中间状态在turing机中意味着顺序和选择关系,顺序关系我们用*表示,选择关系用+。*操作如无异议可以省略,这样做的目的是便于路径计算。process + 调整后的代码如下:  

R(r1) ← m(+=,→→) + m(++,→→) + m(+,→);

  现在代码看起来就简洁多了,继续简化就有点像上下文文法,但有一点请记住,这是语言而不是数据。上面的R和m本身有一定含义,如要执行,也便于宏操作。下面的代码我们增加了一些,也可以说是一些规则。借用函数式的概念,这些规则我们称之抽象。

//@抽象:定义规则
    R(r1) ← m(+=,→→) + m(++,→→) + m(+,→);
    R(r2) ← m(-=,→→) + m(--,→→) + m(-,→);
    R(r3) ← m(*=,→→) + m(*,→);
    R(r4) ← m(/,→) * (m(/,→) * m(≠[\n],→,*) + m(*,→)*m(≠*/,→,*) + m(=,→)+ m());
    R(r5) ← m(<=,→→) + m(<<=,→→→)+ m(<<,→→)  + m(<,→);
    R(r6) ← m(>=,→→) + m(>>=,→→→)+ m(>>,→→)  + m(>,→);
    R(r7) ← m(|=,→→) + m(||,→→)+ m(|,→);
    R(r8) ← m(&=,→→) + m(&&,→→)+ m(&,→);
    R(r9) ← m([a-zA-Z_],→) + m([a-zA-Z_0-9],→,*);
    //处理数字
    R(r10) ← m([1-9],→) * m(m(. ,→) * m([0-9],→,*) * m(m([eE],→)*(m([+-],→),m())*m([0-9],→,+) + m()),m());
    R(r11,s0→sx) ← m(0,→);
    R(r12,sx→s0) ← m([xX],→)*([0-9A-Fa-f],→,4) + R(r10);

  在上面,我们定义了一些turing规则。现在我编写一些组合规则。假设我们有三种语言,分别叫L1、L2、L3,分别采用上面的一些规则,也可以增加自定义的一些规则,下面是示意。

    R(L1) ← R(r1)+R(r2)+R(r3);
    R(L2) ← R(r1)+R(r7)+R(r8)+R(r5);
    R(L3) ← R(r1)+R(r2)+R(r3)+R(r6)+R(r7);

  现在可以应用这些代码来构建turing机,代码如下:

    T(sym.L1)← R(L1);
    T(sym.L2)← R(L2);
    T(sym.L3)← R(L3);

  至此,我们完成了turing机构建的示意。

 

转载于:https://www.cnblogs.com/qianxj/archive/2012/12/28/2821449.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值