interp
注意,不带cont的a的轻
如果是val是常数const
ex.不带cont的情况,也不带状态state,即无法赋值,传入形式是*(lambda (exp env)*
(const-exp (num) (num-val num))
a. 不带cont的情况,传入形式是*(lambda (exp env)*
(const-exp (num) (num-val num))
b. 带cont的情况,传入形式是*(lambda (exp env cont)*
(const-exp (num) (apply-cont cont (num-val num)))
同时,apply-cont的写法无:
因为根据cont来分,一般num的情况不会引发新的cont。
c.带cont和registers的情况,传入形式是*(lambda ()*
(const-exp (num)
(set! val (num-val num))--赋值了,然后就移交给apply-cont去管
--cont is unchanged
(apply-cont))
同时,apply-cont的写法无:
因为根据cont来分,一般num的情况不会引发新的cont。
如果是val是符号(标记符、或称形参)
ex.不带cont的情况,也不带状态state,即无法赋值,传入形式是*(lambda (exp env)*
(var-exp (var) (apply-env env var))
a. 不带cont的情况,
(var-exp (var) (deref (apply-env env var))) --其中deref表示取得后面表达式表示的那个name的content
b.带cont的情况
(var-exp (var) (apply-cont cont (apply-env env var)))
同时,apply-cont的写法无。
c.带cont和registers的情况
(var-exp (var)
-- (apply-cont cont (apply-env env id)))
(set! val (apply-env env var))
-- cont is unchanged
(apply-cont))
同时,apply-cont的写法无。
如果是val是diff-exp
ex.不带cont的情况,也不带状态state,即无法赋值
(diff-exp (exp1 exp2)
(let ((val1 (value-of exp1 env))
(val2 (value-of exp2 env)))
(let ((num1 (expval->num val1))
(num2 (expval->num val2)))
(num-val
(- num1 num2))))
a. 不带cont的情况,
call by ref,但这里没有体现。遇到这种算式直接计算出num-val的结果了。里面保证了value-of其中的表达式的值肯定是num,不然要报错。应该是一个树枝。
(diff-exp (exp1 exp2)
(let ((val1 (value-of exp1 env))
(val2 (value-of exp2 env)))
(let ((num1 (expval->num val1))
(num2 (expval->num val2)))
(num-val
(- num1 num2)))))
b.带cont的情况
对cont进行了改造,表示了下一步的情况。要求下一步对cont要做一个处理。
(diff-exp (exp1 exp2)
(value-of/k exp1 env
(diff1-cont exp2 env cont)))
apply-cont针对diff1-cont有一个处理:
即对传过来的exp2也进行包裹,并把要对exp2做的事情包裹在diff2-cont中。这是因为diff有两个参数。
(diff1-cont (exp2 saved-env saved-cont)
(value-of/k exp2
saved-env (diff2-cont val saved-cont)))
c.带cont和registers的情况
(diff-exp (exp1 exp2)
--(value-of/k exp1 env (diff1-cont exp2 env cont))
(set! cont (diff1-cont exp2 env cont))
(set! exp exp1)
-- env is unchanged
(value-of/k))
apply-cont针对diff1-cont有一个处理:
这里把exp的值的内容写成了exp2。
(diff1-cont (exp2 saved-env saved-cont)
;--(value-of/k exp2 env (diff2-cont val cont)))
(set! cont (diff2-cont val saved-cont))
(set! exp exp2)
(set! env saved-env)
(value-of/k))
从value_of/k一般是根据exp表达式来选择处理分支的,而apply-cont是根据cont的表达式来选择处理分支的。
上面的ex和a都直接返回了值,而b会要求value_of/k对exp1进行解析。解析完毕后,理论上会得到一个值。这个值到时候要干嘛,是根据当时的cont来决定的。
我先列出b和c的diff2-cont,因为之后也许不会列出了。cont的表达式明显和value_of/k不是一一对应的,cont有自己的情境。
在b中,最终算出了这个值,并要求继续apply-cont来计算,缩小上下文,而不是增长cont的内容。
(diff2-cont (val1 saved-cont)
(let ((num1 (expval->num val1))
(num2 (expval->num val)))
(apply-cont saved-cont
(num-val (- num1 num2)))))
在c中,把值存入val,并把当前上下文给cont。这样会根据cont对新求出的值进行处理。
(diff2-cont (val1 saved-cont)
;; --(apply-cont cont (num-val (- num1 num2)))))
(let ((num1 (expval->num val1))
(num2 (expval->num val)))
(set! cont saved-cont)
(set! val (num-val (- num1 num2)))
(apply-cont)))
如果是val是zero?-exp
ex:
递归算出表达式的值来,然后将值变成value,判断value,然后返回一个bool-val的表达式。
(zero?-exp (exp1)
(let ((val1 (value-of exp1 env)))
(let ((num1 (expval->num val1)))
(if (zero? num1)
(bool-val #t)
(bool-val #f)))))
a:
(zero?-exp (exp1)
(let ((val1 (value-of exp1 env)))
(let ((num1 (expval->num val1)))
(if (zero? num1)
(bool-val #t)
(bool-val #f)))))
b:
判断是否是zero要分为两步,由此,我们需要两个zero的上下文步骤。首先先求值。
(zero?-exp (exp1)
(value-of/k exp1 env
(zero1-cont cont)))
对于zero1,apply-cont那边的写法是:
(zero1-cont (saved-cont)
(apply-cont saved-cont
(bool-val
(zero? (expval->num val)))))
就不增加cont了,读取下一个cont,并给有一个bool-val值。
c.
(zero?-exp (exp1)
-- (value-of/k exp1 env (zero1-cont cont))
(set! cont (zero1-cont cont))
(set! exp exp1)
(value-of/k))
对于zero1,apply-cont那边的写法是:
(zero1-cont (saved-cont)
;;-- (apply-cont cont
;;-- (bool-val
;;-- (zero? (expval->num val))))
(set! cont saved-cont)
(set! val (bool-val (zero? (expval->num val))))
(apply-cont))
如果是val是if-exp
ex:
获得一个bool值,并非根据bool值返回接下来要走那一条路。
(if-exp (exp1 exp2 exp3)
(let ((val1 (value-of exp1 env)))
(if (expval->bool val1)
(value-of exp2 env)
(value-of exp3 env))))
a:
与ex相同
b:
上下文要走两次,因为也要做2件事。
(if-exp (exp1 exp2 exp3)
(value-of/k exp1 env
(if-test-cont exp2 exp3 env cont)))
apply-cont:
(if-test-cont (exp2 exp3 saved-env saved-cont)
(if (expval->bool val)
(value-of/k exp2 saved-env saved-cont)
(value-of/k exp3 saved-env saved-cont)))
c:
(if-exp (exp1 exp2 exp3)
;; (value-of/k exp0 env (if-test-cont exp2 exp3 env cont))
(set! cont (if-test-cont exp2 exp3 env cont))
(set! exp exp1)
(value-of/k))
apply-cont:
(if-test-cont (exp2 exp3 saved-env saved-cont)
(set! cont saved-cont)
(if (expval->bool val)
(set! exp exp2)
(set! exp exp3))
(set! env saved-env)
(value-of/k))
最后一步不是apply-cont,而是value-of/k。diff的第一步也是这样。
如果val是let
ex:
一个树枝,求值exp1,然后得到的值和符号var关联在一起(扩展环境)。同时,直接求值处理let后面的body部分的内容。就是接在in后面的那部分内容。
(let-exp (var exp1 body)
(let ((val1 (value-of exp1 env)))
(value-of body
(extend-env var val1 env))))
a:
类似,但是v1作为一个表达式exp1的求值结果,newref表示把v1存入了这个地方,并把这个地方的那个name告知了var。
环境部分的函数的写法可以参考:
https://github.com/racket/eopl/blob/master/tests/chapter4/implicit-refs/environments.rkt
(let-exp (var exp1 body)
(let ((v1 (value-of exp1 env)))
(value-of body
(extend-env var (newref v1) env))))
b:
让exp1进行求值,对于cont的话,增加,增加了一个要赋值的上下文。这个上下文将取得最后得到的那个val的值,也就是这个函数value-of/k的结果。
(let-exp (var exp1 body)
(value-of/k exp1 env
(let-exp-cont var body env cont)))
针对下一步的apply-cont:
(let-exp-cont (var body saved-env saved-cont)
(value-of/k body
(extend-env var val saved-env) saved-cont))
c:
(let-exp (var exp1 body)
;;-- (value-of/k rhs env (let-exp-cont id body env cont))
(set! cont (let-exp-cont var body env cont))
(set! exp exp1)
(value-of/k))
apply-cont:
(let-exp-cont (var body saved-env saved-cont)
;;-- (value-of/k body (extend-env id val env) cont)
(set! cont saved-cont)
(set! exp body)
(set! env (extend-env var val saved-env))
(value-of/k))
如果val是proc-exp
ex:
其实不用做太多,直接变成proc-val等待处理。话说这个procedure其实也是一个标识符,用于把一个数据结构,比如列表中的某位的表达式当作是一个调用其他值的函数,就是用于标识一个proc结构的。一个proc结构里会有一个procedure,类似(procedure ba la ba )这样的。
由此也可以知道,procedure如果是一个构造函数,它会取两个值(body env),然后再取一个值(val ,也就是实参)。
而proc如果是一个构造函数,它会只取一个val,因为它自己已经包含了procedure,所以直接把procedure用在val上即可。
(proc-exp (var body)
(proc-val (procedure var body env)))
a:
同上
b:
(proc-exp (var body)
(apply-cont cont
(proc-val (procedure var body env))))
有趣的是,不仅没有新的上下文产生,而且出现了一个之前没有见过的结构,即这个proc-val其实并不能在apply-cont中找到,而如果cont没有的话,会直接去取value的值。而这个val明显肯定可以在后续被传入一个val,然后继续下去。这刚好达到了实参进入的效果。
当然,以上只是猜测,还是慢慢验证比较好,比如
proc-val是什么?它是一个便捷的写法表示,表示后面是一个proc。
如果遇到了真正的proc,其实是遇到了一个call-exp。这个结构肯定是内嵌在这个里面的。
所以我们接下来就说call-exp。
c:
啊!看了下面这个就能明白了。apply-cont只是继续下去了。cont找到最近的cont来处理,然后会用到需要被求值的val。这说明proc-val只是一个求值的过程中的一环,无需多说。
(proc-exp (var body)
;; --(apply-cont cont (proc-val (procedure bvar body env))
(set! val (proc-val (procedure var body env)))
(apply-cont))
如果val是call-exp
ex:
树枝。两步多出来的。
(call-exp (rator rand)
(let ((proc (expval->proc (value-of rator env)))
(arg (value-of rand env)))
(apply-procedure proc arg)))
附上apply-procedure
(define apply-procedure
(lambda (proc1 arg)
(cases proc proc1
(procedure (var body saved-env)
(value-of body (extend-env var arg saved-env))))))
body提前,内里的东西才是最早形成(接近输入文本)的那部分。(闭包的效果),其中var这个形参和arg这个实参进行了绑定(环境扩张)。之后就是求值body的内容了。
a:
和ex一样。
乍一看,apply-procedure部分有很大不同。但其实一样,只是加了一个输出的debug。
(define apply-procedure
(lambda (proc1 arg)
(cases proc proc1
(procedure (var body saved-env)
(let ((r (newref arg)))
(let ((new-env (extend-env var r saved-env)))
(when (instrument-let)
(eopl:printf
"entering body of proc ~s with env =~%"
var)
(pretty-print (env->list new-env))
(eopl:printf "store =~%")
(pretty-print (store->readable (get-store-as-list)))
(eopl:printf "~%"))
(value-of body new-env)))))))
;; store->readable : Listof(List(Ref,Expval))
;; -> Listof(List(Ref,Something-Readable))
(define store->readable
(lambda (l)
(map
(lambda (p)
(list
(car p)
(expval->printable (cadr p))))
l)))
b:
既然有两部多出来的,那肯定会有函数值计算和参数值的计算。分为两步。那么要做的事情就先放入rator_cont中,作为上下文储存起来。等到时间允许起来之后,就其实就是把value-of/k rator env 得到的值,作为放入rator-cont的这个上下文做完之后要做的事情,作为它的参数。
(call-exp (rator rand)
(value-of/k rator env
(rator-cont rand env cont)))
apply-cont的写法:
apply-cont的参数一直都是(val 和cont),然后根据cont做一些操作。这里的值,就是后来给予rator_cont这个函数的值。这个值是rator。
(rator-cont (rand saved-env saved-cont)
(value-of/k rand saved-env
(rand-cont val saved-cont)))
在这里算的时候,就直接获得了函数,然后运用函数。这里的val是rand部分的值,所以刚好是实参。
(rand-cont (val1 saved-cont)
(let ((proc (expval->proc val1)))
(apply-procedure/k proc val saved-cont)))
apply-procedure如下:
(define apply-procedure/k
(lambda (proc1 arg cont)
(cases proc proc1
(procedure (var body saved-env)
(value-of/k body
(extend-env var arg saved-env)
cont)))))
返回的内容差不多,就是把var和arg绑定。然后继续求值body。
c:
(call-exp (rator rand)
;; --(value-of/k rator env (rator-cont rand env cont))
(set! cont (rator-cont rand env cont))
(set! exp rator)
(value-of/k))
(rator-cont (rand saved-env saved-cont)
;; --(value-of/k rand env (rand-cont val cont))
(set! cont (rand-cont val saved-cont))
(set! exp rand)
(set! env saved-env)
(value-of/k))
(rand-cont (rator-val saved-cont)
(let ((rator-proc (expval->proc rator-val)))
;; --(apply-procedure rator-proc rator-val cont)
(set! cont saved-cont)
(set! proc1 rator-proc)
(set! val val)
(apply-procedure/k)))
(define apply-procedure/k
(lambda ()
(cases proc proc1
(procedure (var body saved-env)
(set! exp body)
(set! env (extend-env var val saved-env))
(value-of/k)))))
如果val是letrec-exp
ex的情况是:
这个letrec的写法是:
letrec double(x) =if zero?(x) then 0 else -((double -(x,1)),-2) in (double 6)
其中(double 6)是body,double是name,x是var ,if到in中间那一段是body。
(letrec-exp (p-name b-var p-body letrec-body)
(value-of letrec-body
(extend-env-rec p-name b-var p-body env)))
其中这个extend-env-rec是这样处理的:
(define apply-env
(lambda (env search-sym)
(cases environment env
(empty-env ()
(eopl:error 'apply-env "No binding for ~s" search-sym))
(extend-env (var val saved-env)
(if (eqv? search-sym var)
val
(apply-env saved-env search-sym)))
(extend-env-rec (p-name b-var p-body saved-env)
(if (eqv? search-sym p-name)
(proc-val (procedure b-var p-body env))
(apply-env saved-env search-sym))))))
a:
(letrec-exp (p-names b-vars p-bodies letrec-body)
(value-of letrec-body
(extend-env-rec* p-names b-vars p-bodies env)))
(begin-exp (exp1 exps)
(letrec
((value-of-begins
(lambda (e1 es)
(let ((v1 (value-of e1 env)))
(if (null? es)
v1
(value-of-begins (car es) (cdr es)))))))
(value-of-begins exp1 exps)))
(assign-exp (var exp1)
(begin
(setref!
(apply-env env var)
(value-of exp1 env))
(num-val 27)))
(define apply-env
(lambda (env search-var)
(cases environment env
(empty-env ()
(eopl:error 'apply-env "No binding for ~s" search-var))
(extend-env (bvar bval saved-env)
(if (eqv? search-var bvar)
bval
(apply-env saved-env search-var)))
(extend-env-rec* (p-names b-vars p-bodies saved-env)
(let ((n (location search-var p-names)))
;; n : (maybe int)
(if n
(newref
(proc-val
(procedure
(list-ref b-vars n)
(list-ref p-bodies n)
env)))
(apply-env saved-env search-var)))))))
b:没有这个函数,b是简化版本的
c:
(letrec-exp (p-name b-var p-body letrec-body)
;; --(value-of/k letrec-body
;; -- (extend-env-rec proc-name bvar proc-body env)
;; -- cont)
(set! exp letrec-body)
(set! env
(extend-env-rec p-name b-var p-body env))
(value-of/k))
(define apply-env
(lambda (env search-sym)
(cases environment env
(empty-env ()
(eopl:error 'apply-env "No binding for ~s" search-sym))
(extend-env (var val saved-env)
(if (eqv? search-sym var)
val
(apply-env saved-env search-sym)))
(extend-env-rec (p-name b-var p-body saved-env)
(if (eqv? search-sym p-name)
(proc-val (procedure b-var p-body env))
(apply-env saved-env search-sym))))))
总结
写到这里,基本对比了各个写法。
c其实是为了支持更好的流程控制,比如为了实现goto或error来产生的一种。因为它的所有内容似乎都可以切断?因为都存在寄存器上,并随时可以开始。没想到这件事竟然如此简单。
本来以为尾递归这件事只有符合了一定要求的内容才能实现,没有想过通过语言处理的方式,仅仅通过符号和顺序之间的结合,就可以做到,实在是太惊人了。