function parameter-passing convertion

这里先对一些需要的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))))])))

Unknown's avatarAbout bracksun
If opportunity of lifetime knocks, are you ready to open the door?

Leave a comment

Design a site like this with WordPress.com
Get started