我正在使用DrRacket環境來嘗試Scheme語言。爲什麼在Scheme中不評估?
我定義的總和+ 1如下:
(define sum+1 '(+ x y 1))
我想知道爲什麼下面的表達式不計算:
(let ([x 1] [y 2]) (eval sum+1))
,而這樣做返回正確的值:
(define x 1)
(define y 2)
(eval sum+1)
我正在使用DrRacket環境來嘗試Scheme語言。爲什麼在Scheme中不評估?
我定義的總和+ 1如下:
(define sum+1 '(+ x y 1))
我想知道爲什麼下面的表達式不計算:
(let ([x 1] [y 2]) (eval sum+1))
,而這樣做返回正確的值:
(define x 1)
(define y 2)
(eval sum+1)
eval
沒有,除非是詞法變量是在同一個表達式創建了詞法變量的工作:
#!r7rs
(import (scheme base)
(scheme eval))
(define env (environment '(scheme base)))
(let ((x 10))
(eval 'x env)) ; ERROR! `x` is not defined
你可以把它看作eval
總是在發生頂層與環境的全局綁定你傳遞給第二個參數。你可以從你的詞法環境值傳遞這樣也許絕招吧:
(eval '(let ((x 10))
x)
env) ; ==> 10
(let ((x 10))
(eval `(let ((x ,x))
x)
env) ; ==> 10
的時候最Scheme實現運行代碼局部變量通常堆棧中分配。因此,一些想象這樣的代碼:
(define (test v)
(display v)
(newline)
(eval 'v))
可能會在運行時變成這樣:
(define (test 1 #f) ; indicates 1 argument, no rest
(display (ref 0)) ; fetches first argument from stack
(newline)
(eval 'v)) ; but what is v?, certainly not the first argument
您也可以使角落的情況。如果你變異會發生什麼?
(define (test v)
(eval '(set! v 10))
v)
到eval
結構可能來自用戶的輸入,所以它不是很明顯,v
被突變,也有很多編制方案的實施需要那麼它需要知道的代碼運行的是v
需要特殊的前處理該變異不同的變量治療,但它不是可確定的,因爲(set! v 10)
可能來自數據庫或用戶輸入。因此,通過不包含本地綁定,您可以節省很多麻煩,並且語言更容易優化和編譯。
有Lisp語言只能解釋,因爲它允許傳遞宏作爲第一類對象。這些語言在編譯時無法推理。
與let
不起作用的命令的原因是let
創建局部變量。這意味着它創建的變量不能從任何地方訪問 - 只能從let
的正文參數中訪問。
在你的榜樣,你定義:
(define sum+1 '(+ x y 1))
然後,你就控制了:
(let ([x 1] [y 2]) (eval sum+1))
這不起作用,因爲x
和y
只被在eval
聲明中定義的,而不是內程序sum+1
。這看起來可能與直覺相反,但它可以防止其他輸入出現很多錯誤。
你的第二個例子是:
(define x 1)
(define y 2)
(eval sum+1)
這不工作,因爲x
和y
是全局定義。這意味着他們可以在任何地方和任何地方訪問。然後將它們應用於sum+1
定義並可以打印。請回答您的任何問題或反饋!
非常感謝您的回答,因此'eval'無法在所調用的範圍內看到定義,而只能看到全局範圍內的定義。我發現這種行爲很奇怪,因爲其他過程可以訪問覆蓋全局範圍的本地範圍,並且由於eval本身是一個過程而不是特殊形式,它應該像所有其他過程一樣行爲!有沒有理由證明這種行爲? –
我不完全確定爲什麼eval不像其他程序那樣行事,但這是我最好的猜測:eval是一個特定的函數,要求對某些東西進行評估,所以有可能不是詢問(就像其他函數一樣),「我有什麼信息可以解決這個問題?」它問道:「我給了什麼信息來解決這個問題?」意思是,它需要特定的指令,而不是全局調用的過程。 (對不起,如果這沒有什麼意義,我的腦海中聽起來很合理。) –
值得考慮一下'eval' *能夠*看到詞法綁定的實現是如何工作的:特別是應該如何'(eval(讀))工作?編譯器如何工作? – tfb
這在[球拍指南](https://docs.racket-lang.org/guide/eval.html)中有相當好的解釋。 – molbdnilo