function parameter-passing convertion
July 28, 2016 Leave a comment
这里先对一些需要的helper函数做定义。这里对于环境(environment)和闭包(closure)的定义在于,使得解释器的实现与数据结构无关。(我现在用的函数表示,当然完全可以用list代替)
(define (empty-env)
(lambda (y) (error 'value-of "unbound variable ~s" y)))
(define (apply-env env x)
(env x))
(define (extend-env x val env)
(lambda (y)
(if (eqv? x y)
val
(apply-env env y))))
(define (make-closure x body env interp)
(lambda (var) (interp body (extend-env x var env))))
(define (apply-closure e1 e2)
(e1 e2))
这里给出call-by-value解释器的定义,对于函数((lambda (x) body) exp),在解释body的时候,我们把x和exp bind在一起放在环境中。这个时候,exp是已经evaluate过的。也就是说每次在对函数进行evaluate的时候,参数在bind以前就已经evaluate。
同时,可以看到对于exp解释的时候,在环境中找到exp的box,通过unbox取出值,然后再box生成一个新的box structure。这样在对函数进行解释的时候,传入的value是之前环境中value的一个复制(copy)。
(define val-of-cbv
(lambda (exp env)
(match exp
[`,b #:when (boolean? b) b]
[`,n #:when (number? n) n]
[`,y #:when (symbol? y) (unbox (apply-env env y))]
[`(zero? ,n) (zero? (val-of-cbv n env))]
[`(sub1 ,n) (sub1 (val-of-cbv n env))]
[`(* ,n1 ,n2) (* (val-of-cbv n1 env) (val-of-cbv n2 env))]
[`(if ,test ,conseq ,alt) (if (val-of-cbv test env)
(val-of-cbv conseq env)
(val-of-cbv alt env))]
[`(begin2 ,e1 ,e2) (begin (val-of-cbv e1 env) (val-of-cbv e2 env))]
[`(set! ,x ,v) (set-box! (apply-env env x) (val-of-cbv v env))]
[`(random ,n) (random (val-of-cbv n env))]
[`(lambda (,x) ,body) (make-closure x body env val-of-cbv)]
[`(,rator ,rand) (apply-closure (val-of-cbv rator env)
(box (val-of-cbv rand env)))])))
第二种解释器,是基于call-by-reference,函数的参数在进行bind的时候,传进去的是数据的引用,而不是数据的value。
我们对
(,rator ,sym) #:when (symbol? sym)
pass的是symbol的情况做特殊处理:在环境中找到sym所对应的box,直接把box和函数的参数进行bind。这样,如果box的内容被修改,相应的,环境中sym所对应的值都会改变。
(define val-of-cbr
(lambda (exp env)
(match exp
[`,b #:when (boolean? b) b]
[`,n #:when (number? n) n]
[`,y #:when (symbol? y) (unbox (apply-env env y))]
[`(zero? ,n) (zero? (val-of-cbr n env))]
[`(sub1 ,n) (sub1 (val-of-cbr n env))]
[`(* ,n1 ,n2) (* (val-of-cbr n1 env) (val-of-cbr n2 env))]
[`(if ,test ,conseq ,alt) (if (val-of-cbr test env)
(val-of-cbr conseq env)
(val-of-cbr alt env))]
[`(begin2 ,e1 ,e2) (begin (val-of-cbr e1 env) (val-of-cbr e2 env))]
[`(set! ,x ,v) (set-box! (apply-env env x) (val-of-cbr v env))]
[`(random ,n) (random (val-of-cbr n env))]
[`(lambda (,x) ,body) (make-closure x body env val-of-cbr)]
[`(,rator ,sym) #:when (symbol? sym) (apply-closure (val-of-cbr rator env)
(apply-env env sym))]
[`(,rator ,rand) (apply-closure (val-of-cbr rator env)
(box (val-of-cbr rand env)))])))
第三种解释器,是call-by-name,这里用到了一个解释器通用的trick --thunk。也就是说((lambda(x) exp),在bind的时候,exp并没有被解释,直到exp被调用的时候。
(lambda() (val-of-cbname rand env))
我们并没有直接evaluate rand的值,而是把它做成一个没有参数的函数和rator的参数bind,直到这个参数被调用的时候,才对rand进行evaluate。
(define val-of-cbname
(lambda (exp env)
(match exp
[`,b #:when (boolean? b) b]
[`,n #:when (number? n) n]
[`,y #:when (symbol? y) ((unbox (apply-env env y)))]
[`(zero? ,n) (zero? (val-of-cbname n env))]
[`(sub1 ,n) (sub1 (val-of-cbname n env))]
[`(* ,n1 ,n2) (* (val-of-cbname n1 env) (val-of-cbname n2 env))]
[`(if ,test ,conseq ,alt) (if (val-of-cbname test env)
(val-of-cbname conseq env)
(val-of-cbname alt env))]
[`(random ,n) (random (val-of-cbname n env))]
[`(lambda (,x) ,body) (make-closure x body env val-of-cbname)]
[`(,rator ,rand) (apply-closure (val-of-cbname rator env)
(box (lambda () (val-of-cbname rand env))))])))
最后一种方法是call-by-need, 也叫做lazy evaluation。对于call-by-name,每次evaluate参数的时候,他的值都会重新被evaluate一次。但是call-by-need的做法,就是第一次进行evaluate,再把evaluate的结果存在环境中。这样就可以只进行一次evaluation。
(define (unbox/init-set box-case)
(let ([val ((unbox box-case))])
(begin (set-box! box-case (lambda() val)) val)))
(define val-of-cbneed
(lambda (exp env)
(match exp
[`,b #:when (boolean? b) b]
[`,n #:when (number? n) n]
[`,y #:when (symbol? y) (unbox/init-set (apply-env env y))]
[`(zero? ,n) (zero? (val-of-cbneed n env))]
[`(sub1 ,n) (sub1 (val-of-cbneed n env))]
[`(* ,n1 ,n2) (* (val-of-cbneed n1 env) (val-of-cbneed n2 env))]
[`(if ,test ,conseq ,alt) (if (val-of-cbneed test env)
(val-of-cbneed conseq env)
(val-of-cbneed alt env))]
[`(random ,n) (random (val-of-cbneed n env))]
[`(lambda (,x) ,body) (make-closure x body env val-of-cbneed)]
[`(,rator ,rand) (apply-closure (val-of-cbneed rator env)
(box (lambda () (val-of-cbneed rand env))))])))