LISP解释器的本质载体就是这两个简单的程序:eval和apply,解释器就像其他普通程序一样处理的是Data,只不过这里的data要符合解释器定义的语言规则(也就是编程语言表达式expression)
用户输入一段代码也就是expression,先进入到eval,eval干的事就是:分。 分完之后,将分好的多个data传给apply。apply干的事就是合,合在一起之后,将合的data再传给eval。eval在重复上面的逻辑继续分,然后传给apply。他俩就这么一分一合,直到data成为无法分/合的归一。
1)解释器的本质就是分/合的递归,直到无法分/合的归一。
2)eval代表分,apply代表合
3)物理视角的伪代码:
eval的分:将expression分为procedure和arguments两句,再传给apply
(apply
(operator expression)
(operands expression))
apply的合:将算法和数据合并为一句到eval中去
(eval expression environment)
;; expression代表算法
;; environment代表数据
4)procedure就是data,data就是procedure,色即是空,空即是色。
色隐喻着procedure,空隐喻着data。
同时空又有一层表达,物质的存在不是有更小的物质组合而成,而是有什么都没有的虚空模拟而成。
data的最基本概念为Pair,pair的定义是基于cons/car/cdr这三个procedure。
procedure的构成又是基于list,list的本质就是pair的变形,
看代码:
1.data由procedure模拟而成:
(define (cons x y)
(lambda (m) (m x y)))
(define (car z)
(z (lambda (p q) p)))
(define (cdr z)
(z (lambda (p q) q)))
2.procedure由data模拟而成:
(define (make-procedure parameters body env)
(list 'procedure parameters body env))
5)信息,是procedure和data的统一视角。
6)计算机科学的第一性原理就是解释器,解释器的第一性原理就是信息的分/合的递归。见下图:

7)抽象到宇宙观,宇宙的第一性原理也是信息的分/合,宇宙的本质就是信息,宇宙的行为就是信息的分/合。从宏观来看,宇宙就是在做着如同呼吸一样的扩张(分)与收缩(合)
8)所以,我们解决任何问题的思路,无非就是分/合。一切尽在分/合之间。
What - lisp解释器的本质update:定位出解释器的概念体系。
1)lisp的解释器由eval/apply这两个procedure来承载。
2)从抽象视角来看,eval/apply本质上就是再实现lambda演算中的Beta-Reduction,即,((λx.M) E) → (M [x:= E]),Replacing the bound variables with the argument expression in the body of the abstraction.
3)Beta-Reduction本质上就是在做组装,将参数装入到Lambda的body中。
4)计算的本质就是Beta-Reduction的这一组装/变形的动作。
5)只不过Eval/Apply将这个组装动作进行了分解:
首先,一个像这样的最简单的application:((lambda (x) (x)) 3),作为expression传入到Eval中。
然后,Eval对这个expression做分解,将((lambda (x) (x)) 3)分解为连个部分:
procedure部分:(operator expression) 对应着 (lambda(x) (x))
argument部分:(operands expression) 对应着 3
再将这两部分传给Apply
Apply干了两件事:1.将procedure提取出body部分:(x),2.将procedure中的parameter:x,绑定argument:3,也就是将x=3存入到一个table中,即,env出现。
Apply再将body和env传入给Eval
Eval,拿到body和env干的事,才是真正的组装,即,发现body中遇到的变量x,去evn中寻找x对应的value:3,然后将value装入到body中(x)就变成了最终的结果:3.
所以说,eval/apply将lambda演算中的Bate-Reduction进行了分解动作。
6)Why?为什么要分解动作?难道不能用一个procedure直接的进行Bata-Reduction的组装吗?即,直接将expression中的operand组装到operator中去。
答案是,因为要兼容赋值模型Assignment,eval运行时并不知道一个procedure的body中是否包含Assignment,所以要兼容,当作可能有Assignment来处理。Assignment就不能直接将operand直接装入到operator中去,因为body中的变量可能随时被别人修改。所以要引入Env来由一个中间环节来存储body中的变量。
当然,如果能够确定,要处理的所有expression都没有assignment,那么解释器就可以简化成,不再需要evn。
7)所以,抽象来看解释器的精髓点就是Beta-Reduction的实现。
8)当然,解释器的非核心部分还实现了Lambda演算中的其他概念:变量variable,抽象体abstraction,应用application。
9)所以,更全面,但是没有精髓感的说,抽象来看解释器就是Lambda演算的实现。
10)Lisp解释器与Lambda演算的对应关系:
1.变量Variable的实现对应的代码是:
((variable? exp) (lookup-variable-value exp env))
2.抽象体Abstraction的实现对的代码是:
((lambda? exp)
(make-procedure (lambda-parameters exp)
(lambda-body exp)
env))
3.应用Application的实现对应的代码是:
((application? exp)
(apply (eval (operator exp) env)
(list-of-values (operands exp) env)))
11)Lisp解释器为了让语言更加高效而加入的衍生规则,当然这些衍生规则都可以用上面的那三个原则来推演而得。但是为了构建逻辑更加高效,而增加的中间层。
1.原始元素:数字(1,2,3等)和数字运算操作符(+ - 等)
((self-evaluationg? exp) exp)
2.字符串
((quoted? exp) (text-of-quotation exp))
3.赋值语句
((assignment? exp) (eval-assignment exp env))
4.定义
((definition? exp) (eval-definition exp env))
5.条件语句
((if? exp) (eval-if exp env))
;; and
((cond? exp) (eval (cond->if exp) env))
6.begin语句
((begin? exp)
(eval-sequence (begin-actions exp) env))
网友评论