Programming languages 备忘录、解释器和闭包

Programming languages 备忘录、解释器和闭包,programming-languages,scheme,interpreter,memoization,Programming Languages,Scheme,Interpreter,Memoization,所以我正在试验,并在scheme中创建了一种编程语言。我也为它构建了一个解释器,这是下面的大部分代码 我想重写解释器,以便它使用较小的环境构建闭包,即,在构建闭包时,它使用与当前环境类似的环境,但只保存闭包函数部分中的自由变量。我正在学习记忆,但这让人困惑 编辑:我现在使用的球拍相当于这个,所以如果那里比较容易,你应该给我一些建议 (define-struct var (string)) ;; a variable, e.g., (make-var "foo") (define-struct i

所以我正在试验,并在scheme中创建了一种编程语言。我也为它构建了一个解释器,这是下面的大部分代码

我想重写解释器,以便它使用较小的环境构建闭包,即,在构建闭包时,它使用与当前环境类似的环境,但只保存闭包函数部分中的自由变量。我正在学习记忆,但这让人困惑

编辑:我现在使用的球拍相当于这个,所以如果那里比较容易,你应该给我一些建议

(define-struct var (string)) ;; a variable, e.g., (make-var "foo")
(define-struct int (num)) ;; a constant number, e.g., (make-int 17)
(define-struct add (e1 e2)) ;; add two expressions
(define-struct fun (name formal body)) ;; a recursive 1-argument function
(define-struct closure (fun env)) ;; closures (made at run-time)

(define (envlookup env str)
    (cond [(null? env) (error "unbound variable during evaluation" str)]
        [(equal? (caar env) str) (cdar env)]
        [#t (envlookup (cdr env) str)]))

(define (eval-prog p)
    (letrec
        ([f (lambda (env p)
            (cond [(var? p) (envlookup env (var-string p))]
                    [(int? p) p]
                    [(add? p) (let ([v1 (f env (add-e1 p))]
                                    [v2 (f env (add-e2 p))])
                                        (if (and (int? v1) (int? v2))
                                            (make-int (+ (int-num v1) (int-num v2)))
                                            (error "TTPL addition applied to non-number")))]
                    [(fun? p) (make-closure p env)]
                    [(closure? p) p]
                    [#t (error "bad TTPL expression")]))])
    (f () p)))

如果您不介意阅读一些Haskell,可以演示如何创建闭包:当遇到
(lambda…
表达式时,它的闭包只需设置为当前环境(从名称到值的绑定列表)。当计算lambda时,其主体将在该闭包的上下文中加上参数绑定(当然不是当前环境)进行计算


听起来您想要做的是剔除成为闭包的环境,也许是为了提高效率。为此,您可以在函数中搜索名称,并仅保留参数列表中未显示的名称。但是,这可能会过多,因为lambda使用的每个名称(参数除外)都需要出现在闭包中。因此,我建议您只参考您已经拥有的环境,其中大部分都将被共享。

第一个问题:您的语言中是否存在绑定变异?看起来您目前没有,但可能您正计划添加它。如果你这样做了,那么复制绑定就会打开一个新的蠕虫罐;需要额外的间接寻址

回答您的问题:是的,您当然可以做到这一点,大多数语言实现都可以做到。这与“空间安全”的特性有关,即闭包避免保留对大值的引用,否则可能会收集这些值

实现这一点非常简单:您可能希望通过在程序上进行单个静态传递,用其自由变量对每个表达式进行注释。在Racket中,我可能会构建一个哈希表,将表达式与其自由变量列表相关联


值得一提的是,我可以想象有七种方法,通过这样做,您可能会意外地使您的语言慢了一点:)。

看起来您想要创建类似平面闭包的东西,或者Dybvig称之为“显示闭包”。也就是说,您必须递归地在lambda中找到自由变量,然后创建一个只包含这些自由变量的闭包表示

比如说,

((lambda (x) (lambda (f) (f x))) a)
将创建一个类似于
[code,a]
的闭包

看看Dybvig的,第88页