2011-03-23 44 views
34

好吧,這是一個相當基本的問題:我正在關注SICP視頻,我對define,letset!之間的差異有點困惑。define,let和set之間的區別!

1)據Sussman在視頻中,define被允許附加一個值只能變化一次(REPL中除外),特別是兩行定義不允許。然而,Guile樂意運行此代碼

(define a 1) 
(define a 2) 
(write a) 

和輸出2,如預期。事情是更復雜一點,因爲如果我嘗試這樣做(編輯:上面的定義後)

(define a (1+ a)) 

我得到一個錯誤,而

(set! a (1+ a)) 

是允許的。但我不認爲這是set!define之間的唯一區別:我錯過了什麼?

2)definelet之間的差異讓我更加困惑。理論上我知道let用於綁定局部範圍內的變量。不過,在我看來,這可與define一樣,比如我可以代替

(define (f x) 
    (let ((a 1)) 
     (+ a x))) 

(define (g x) 
    (define a 1) 
    (+ a x)) 

fg工作一樣:尤其是可變a是綁定外g

我可以看到這個有用的唯一方法是let可能有一個更短的範圍,整個函數的定義。儘管如此,我仍然可以添加一個匿名函數來創建必要的範圍,並立即調用它,就像在javascript中一樣。那麼,let的真正優勢是什麼?

+0

必須先縮進'(+一個X)'在F'的'的定義,因爲它是let'的'範圍之內。 – 2011-03-23 14:12:13

+0

你說得對,謝謝 – Andrea 2011-03-23 14:18:50

+0

這取決於你使用的方案系統。 – knivil 2011-03-23 20:23:29

回答

14

你的意思是(+ 1 a)而不是(1+ a)?後者在語法上不合法。由let定義的變量

範圍被綁定到後者,從而

(define (f x) 
    (let ((a 1)) 
    (+ a x))) 

在語法上是可能的,而

(define (f x) 
    (let ((a 1))) 
    (+ a x)) 

不是。

所有變量必須是define d在功能的開始,因此,下面的代碼是可能的:

(define (g x) 
    (define a 1) 
    (+ a x)) 

而這個代碼將產生一個錯誤:

(define (g x) 
    (define a 1) 
    (display (+ a x)) 
    (define b 2) 
    (+ a x)) 

因爲第一定義後的表達意味着沒有其他定義。

set!未定義變量,而是用於爲變量賦值一個新值。因此,這些定義是毫無意義的:

(define (f x) 
    (set! ((a 1)) 
    (+ a x))) 

(define (g x) 
    (set! a 1) 
    (+ a x)) 

set!有效的使用方法如下:

(define x 12) 
> (set! x (add1 x)) 
> x 
13 

雖然它氣餒,因爲計劃是一個功能性的語言。

+8

'1 +'是Scheme某些版本的一個函數,所以這個調用是有效的。 – 2011-03-23 14:11:03

+0

好的,'define'只允許在一個函數的開始處這一事實很有趣。這排除了取消匿名函數來創建函數作用域的可能性,至少在某些情況下。 – Andrea 2011-03-23 14:13:18

+0

@Jeremiah:它與Racket中的add1是否相同,是增加其參數的函數? – 2011-03-23 14:13:46

2

您可以使用比一次更define,但它不是 地道:define意味着要添加一個定義到 環境和set!意味着你是變異某個變量。

我不確定關於Guile以及爲什麼它允許(set! a (+1 a))但是 如果a沒有被定義,那就不應該起作用。通常會使用 define來引入一個新變量,並且僅在稍後使用set! 進行變異。

您可以使用匿名函數的應用程序,而不是let,在 實際上這通常正是let擴展到,它幾乎總是 宏。這是等價的:

(let ((a 1) (b 2)) 
    (+ a b)) 

((lambda (a b) 
    (+ a b)) 
1 2) 

你會使用let的原因是,它更清晰:變量名稱旁邊的值。

在內部定義的情況下,我不確定Yasir是否爲 正確。至少在我的機器上,在R5RS模式下運行球拍並且在 常規模式下允許內部定義出現在 函數定義的中間,但我不確定標準說的是什麼。在任何 的情況下,在很久以後的SICP中,內部定義的姿勢是 深入討論的技巧。在第4章中,探討了如何實現內部定義的互相遞歸,以及它對元環解釋器的實現 意味着什麼。

所以堅持下去! SICP是一本精彩的書,視頻講座非常棒。

28

你的困惑是合理的:'let'和'define'都會創建新的綁定。 「讓」的一個優點是它的含義非常明確;不同的Scheme系統(包括Racket)之間絕對沒有意見分歧,這就意味着什麼是簡單的「let」。

「定義」形式是一個不同的魚水壺。與'let'不同,它不包圍帶括號的正文(綁定有效的區域)。而且,它可能意味着頂級和內部的不同事物。不同的Scheme系統對'define'具有顯着不同的含義。事實上,最近Racket通過添加新的上下文來改變'define'的含義。

另一方面,人們喜歡'定義';它具有較少的縮進,並且它通常具有「do-what-I-mean」範圍,允許遞歸和相互遞歸過程的自然定義。實際上,就在那一天,我被這個咬傷了:)。

最後,'set!「;像'讓','套!'非常簡單:它會改變現有的綁定。

FWIW,理解DrRacket中這些範圍的一種方法(如果您使用的話)是使用「Check Syntax」按鈕,然後將鼠標懸停在各種標識符上以查看它們綁定的位置。

+0

謝謝;我已經編輯了這個問題,以表明我正在使用'set!'** sfter ** defitions。該段中的要點是,我可以根據它以前的值設置一個變量,但是我不能根據它以前的值定義一個變量。 – Andrea 2011-03-24 09:57:57

+0

正確;這是因爲「定義」的範圍包含綁定本身的右側。這很重要,因爲它允許'定義'來創建遞歸過程,這是'let'不可能實現的(除非你使用Y-combinator-like技巧)。 – 2011-03-24 16:37:03

+0

在雞計劃,設置!也可以用來引入新的綁定。這讓探索式編程一次就讓我失望了。例如,(define(foo a)(set!ba))會愉快地運行在新的環境中:csi -e'(define(foo a)(set!b(* a 2)))(foo 42) (顯示b)'。看來那套!將從大多數本地環境走向全局環境,直到找到指定的變量。我正在使用4.9.0.1。 – 2015-03-05 04:22:43

5

John Clements的回答很好。在某些情況下,您可以看到每個版本的Scheme中define的變化情況,這可能會幫助您瞭解發生了什麼。

例如,在切斯計劃8.0(它有自己的define怪癖,ESP WRT R6RS!):

> (expand '(define (g x) 
      (define a 1) 
      (+ a x))) 
(begin 
    (set! g (lambda (x) (letrec* ([a 1]) (#2%+ a x)))) 
    (#2%void)) 

您將看到 「頂級」 定義成爲set!(雖然只是擴大define在某些情況下會改變的東西!),但內部定義(即在另一個塊內的define)變成letrec*。不同的方案將把這個表達擴展到不同的事物。

的MzScheme V4.2.4

> (expand '(define (g x) 
      (define a 1) 
      (+ a x))) 
(define-values 
(g) 
(lambda (x) 
    (letrec-values (((a) '1)) (#%app + a x)))) 
相關問題