2011-11-19 27 views
4

所以我正在試驗,並且在方案中創建了一種編程語言。我也爲它構建了一個解釋器,這是下面的大部分代碼。記憶,口譯員和閉包

我想重寫解釋器,以便在較小的環境下構建閉包,即。在構建閉包時,它使用的環境與當前環境相似,但僅在閉包的函數部分保留變量,該變量是自由變量。我正在學習記憶,但這是令人困惑的。

編輯:我現在正在使用相當於此的球拍,所以如果在那裏更容易,你應該給我建議。

(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))) 

回答

1

第一個問題:你的語言中是否有綁定突變?看起來你目前沒有,但也許你打算添加它。如果你這樣做,那麼複製綁定會打開一堆新的蠕蟲;額外的間接性是必需的。

回答你的問題:是的,你當然可以做到這一點,並且大多數語言實現都可以。這與「安全換空間」的特性有關,即封閉避免保留對可能被收集的大值的引用。

實現這一點非常簡單:您可能希望通過在程序中進行單個靜態傳遞來註釋每個表達式的自由變量。在Racket中,我可能會建立一個將表達式與其自由變量列表相關聯的哈希表。

對於它的價值,我可以想象出七種方式,在這種方式中,您可能會因爲這樣做而意外地使您的語言變得相當慢:)。

0

如果你不介意閱讀一些哈斯克爾,Write Yourself a Scheme in 48 Hours演示瞭如何關閉創建:遇到(lambda ...)表達式時,它的關閉被簡單地設置爲當前環境(綁定從名字到值的列表) 。當一個lambda被評估時,它的body將在該閉包的上下文中加上參數綁定來評估 - 當然不是當前的環境。

這聽起來像你想要做的是挑選成爲封閉的環境,也許爲了效率。爲此,您可以搜索名稱的函數,並僅保留那些不出現在參數列表中的名稱。然而,這可能會過度,因爲每個名稱的lambda使用 - 除了它的參數 - 將需要出現在閉包中。因此,我建議您僅僅提到您已經擁有的環境,而其中大部分都將被共享。

1

它看起來像你想創造像平封閉,或Dybvig稱爲「顯示閉包」的東西。也就是說,你必須遞歸地在你的lambda中找到自由變量,然後創建一個包含那些自由變量的閉包表示。

例如,

((lambda (x) (lambda (f) (f x))) a) 

將創建一個封閉,看起來像[code, a]

看看Dybvig的Three Implementation Models for Scheme,第88頁。