2012-03-31 67 views
3

當我每一次評估以下表達式我得到的值10計劃分配

(((lambda (x) (lambda() (set! x (+ x 10)) x)) 0)) 

但是我只是一個名稱提取上述程序修改,並通過10調用foo每次值增量!

(define foo ((lambda (x) (lambda() (set! x (+ x 10)) x)) 0)) 

有人能解釋一下嗎?

回答

5

您正在調用的函數是一個計數器,每次調用它時都會返回數字10。

在第一種情況下,您每次都創建一個新函數,然後立即調用它,然後放棄函數。所以每次你第一次調用這個計數器的新實例,所以它應該返回10.

在第二種情況下,你創建一次函數並將它賦給一個變量並重復調用同一個函數。既然你調用的是相同的函數,它應該返回10,20,...

2

newacct是正確的,但我想進入(很多)更多的細節,因爲這是一些只是讓我的頭腦很漂亮最近。

我打算使用「環境」和「範圍」這兩個術語來相當鬆散地表示本質上是相同的東西。請記住,該方案是一個lexical scope language

當方案計算一個表達式時,它會在當前環境中查找表達式中任何變量的值。如果在當前環境中找不到任何東西,它會在父環境中查找。如果該值不在父環境中,那麼它將在下一級查找,直到達到頂級(全局)級別,在該級別中它將找到該值或拋出「未綁定變量」錯誤。

無論何時您致電define,您都會將符號與該環境符號表上的值相關聯。因此,如果您在頂層調用define,則會將條目添加到全局符號表中。如果在過程的主體中調用define,則將在該過程的符號表中添加一個條目。

去想一個過程調用define一個好方法是,你正在創建一個由參數,身體的符號表中的條目,並且過程的環境。例如,程序square將有一個條目是這樣的:

(define a 3) 

(define (square x) 
    (* x x)) 

    GLOBAL 
================= 
    a-|-3 
     | 
square-|-{x} 
     | {(* x x)} 
     | {GLOBAL} ---> All the things defined on the global table 

然後如果我打電話給(square a)解釋將在其中square定義環境先來看看它會發現,a與關聯值3.然後x - > 3在正方形體內並且過程返回9.很酷,有道理。

當我們開始在過程中定義幫助程序時,事情會變得有點麻煩,但是您真正需要記住的是,如果在當前環境中找不到與某個符號相關的任何內容,它將向上移動範圍級別直到它。而且,它會在第一場比賽中停止。所以如果有一個當地的x它會比全球的x更喜歡它(相反它會使用當地的x而沒有尋找全球性的)。

接下來,請記住,define只是將名稱添加到符號表中,但set!是實際更改與符號關聯的值的增變器。

所以(define b "blah")在符號表中放入一個條目。 b => "blah"。沒有瘋狂。 set!會改變實際值:

(set! b "foo") 
b => "foo" 

set!不能添加任何表。 (set! c "bar") => UNBOUND VARIABLE C

這是最重要的區別:set!的行爲就像在其他任何過程,如果它沒有找到在當前範圍內的變量,它會檢查逐步更高級別,直到找到一個匹配(或拋出一個錯誤),但define總是添加一個綁定到它被調用的範圍。

好的,所以你瞭解defineset!之間的區別。好。現在談論這個問題。

表達(((lambda (x) (lambda() (set! x (+ x 10)) x)) 0)),作爲newacct指出的那樣,會因爲你每次調用一個新的程序,每次返回相同的值。但是,如果您命名它,則可以跟蹤調用該過程所創建的環境。

(define foo  <--- associated name on the symbol table 
    (lambda (x) <--- scope where x is defined 
     (lambda()   \ 
      (set! x (+ x 10)) |--- body 
      x))    /
     0)  <--- initial value of x 

所以內lambda存在由第一個其中符號x存在於的0。然後set!查找在符號表x的條目的初始值和找到一個在下一創建的環境內升級。一旦它發現它改變了它,在這種情況下,它將在其中找到的值增加10。真正很酷的部分是,因爲您將整個事件與全局符號表中的名稱關聯起來,所以在每次調用之後,該環境仍然存在!這就是爲什麼我們可以做一些很酷的事情,比如實現消息傳遞對象來跟蹤和處理數據!

此外,let特殊形式用於此目的的創建,並且可以構建這樣的更直觀的方式。它應該是這樣的:

(define foo  <--- associated name 
    (let ((x 0)) <--- scope where x is defined & initial x value 
     (lambda()   \ 
      (set! x (+ x 10)) |--- body 
      x)))   /