2017-09-06 39 views
2

我覺得理解這種細微之處可以幫助我理解Scope在Scope中的工作方式。Scheme/Guile:函數內部的自變量重定義

那麼,爲什麼方案錯誤,如果你嘗試做這樣的事情:

(define (func n) 
    (define n (+ 1 n)) 
    n) 

,只出現了錯誤,在運行時調用函數時。

我覺得奇怪的原因是因爲Scheme允許重定義,即使在函數內部也是如此。例如,這沒有給出錯誤,將始終返回預期值5:

(define (func n) 
    (define n 5) 
    n) 

此外,計劃也似乎支持全局空間自重新定義。例如:

(define a 5) 
(define a (+ 1 a)) 

不會導致錯誤,並導致「a」按預期顯示「6」。

那麼爲什麼同樣的事情發生運行時錯誤,當它發生在一個函數內(這支持重定義)?換句話說:爲什麼只有在函數內部自我重定義才起作用?

回答

2

define全球

首先,頂層程序由實現方式的不同部分中比在函數處理並且限定已定義的變量是不允許的。

(define a 10) 
(define a 20) ; ERROR: duplicate definition for identifier 

它可能發生在REPL中,因爲通常重新定義東西,但是在運行程序時這是絕對禁止的。在R5RS中,發生的事情之前沒有詳細說明,也沒有關注,因爲違反規範,它不再是計劃程序,實施者可以自由地做任何他們想做的事情。結果當然是很多未指定的東西獲得了不可移植或不穩定的實現特定行爲。

解決方案:

set!變異綁定:

(define a 10) 
(set! a 20) 

define在一個lambda(功能,讓...)

一個define在拉姆達是完全不同的東西,由完全不同的部分實施處理。它是由宏/特殊形式lambda使得它改寫爲letrec*

一個letrec*letrec用於製造功能遞歸所以名稱必須是可在表達式的計算時間處理。正因爲如此,當你作爲參數傳遞時,它已經隱藏了n。另外,從R6RS開始,實現者需要在綁定評估尚未初始化時發出錯誤信號,這可能會發生。之前R6RS實施者可以自由地做任何他們想要的東西:

(define (func n) 
    (define n (+ n 1)) ; illegal since n hasn't got a value yet! 
    n) 

這實際上就變成了:

(define func 
    (lambda (n) 
    (let ((n 'undefined-blow-up-if-evaluated)) 
     (let ((tmpn (+ n 1))) 
     (set! n tmpn)) 
     n))) 

現在,編譯器可能會看到它違反了在編譯時的規範,但許多實現不知道在它運行之前。

(func 5) ; ==> 42 

如果實施者在書本上有良好的品味,完美的結果在R5RS。 在版本的區別,你說的作品是,這並不違反前身體評估n的規則:

(define (func n) 
    (define n 5) 
    n) 

變爲:

(define func 
    (lambda (n) 
    (let ((n 'undefined-blow-up-if-evaluated)) 
     (let ((tmpn 5)) ; n is never evaluated here! 
     (set! n tmpn)) 
     n))) 

解決方案

使用非相沖突的名稱

(define (func n) 
    (define new-n (+ n 1)) 
    new-n) 

使用let。當表達式得到評估時,它沒有自己的綁定:

(define (func n) 
    (let ((n (+ n 1))) 
    n))