[英]SICP Exercise 4.6: Implementing let in the evaluator as a derived expression based on lambda
我試圖編寫一個函數來轉換形式的let
表達式:
(let ((var1 exp1) ... (varn expn)) body)
轉換為以下形式的(等效) lambda
表達式:
((lambda (var1 ... varn) body) exp1 ... expn)
我發現該問題的大多數解決方案似乎都是錯誤的,有人可以確認是這種情況嗎?
;; let expression
(define (let-vars expr) (map car (cadr expr)))
(define (let-inits expr) (map cadr (cadr expr)))
(define (let-body expr) (cddr expr))
(define (let->combination expr)
(cons (make-lambda (let-vars expr) (let-body expr))
(let-inits expr)))
我相信let->combination
將返回這種形式的列表:
((lambda (var1 ... varn) body) (exp1 ... expn))
但是,我認為它應該返回的是以下形式的列表:
((lambda (var1 ... varn) body) exp1 ... expn)
以某種方式,是否應該在let->combination
使用對apply函數的調用? let->combination
應該如何修改以包含apply
?
我相信 let-combination將返回這種形式的列表:
((lambda (var1 ... varn) body) (exp1 ... expn))
但是,我認為它應該返回的是以下形式的列表:
((lambda (var1 ... varn) body) exp1 ... expn)
有好消息,有壞消息。 壞消息是你的信念是錯誤的。 好消息是您的想法是正確的。 也就是說,您確實需要結果((lambda …) …)
,這就是該代碼產生的結果。
此Scheme代碼不需要任何特殊的解釋器或任何內容,因此您可以進行測試,而不會遇到任何實際問題。 讓我們再來看一下定義:
(define (let-vars expr) (map car (cadr expr)))
(define (let-inits expr) (map cadr (cadr expr)))
(define (let-body expr) (cddr expr))
(define (let->combination expr)
(cons (make-lambda (let-vars expr) (let-body expr))
(let-inits expr)))
那么,例如(let->combination '(let ((x 34)) (list x)))
會發生什么? 您將評估:
(cons (make-lambda (let-vars expr) (let-body expr))
(let-inits expr))
這些零件會產生什么? (make-lambda …)
創建您期望的lambda表達式:
(lambda (x) (list x))
和(let-inits …)
返回:
(34)
現在的問題是(cons '(lambda (x) (list x))
'(34))是什么? 您可以輕松地進行測試; 它的:
((lambda (x) (list x)) 34)
這可能是因為對cons
工作方式有些困惑。 列表可以是:空列表'()
或由cons生成的一對,其對講 車是列表的元素 ,而cdr是列表的其余部分 。 從而:
(cons 1 '()) ;=> (1)
(cons 1 '(2)) ;=> (1 2)
(cons '(lambda (x) x) '(34)) ;=> ((lambda (x) x) 34)
我相信let-combination將返回這種形式的列表:
((lambda (var1 ... varn) body) (exp1 ... expn))
…以某種方式,在let組合中不應該使用對apply函數的調用嗎? 應該如何修改let-combination以使其適用?
如我們所見,代碼實際上產生了您想要的結果。 如果它實際上產生了類似((lambda (var1 ... varn) body) (exp1 ... expn))
,那么您可能希望使用apply
。 最簡單的方法是生產
(apply (lambda (var1 ... varn) body) (list exp1 ... expn))
請注意,我們添加了apply
以及對list
的調用。 這將需要更改let->combination
的定義。 一種可能性是:
(define (let->combination expr)
(cons 'apply
(cons (make-lambda (let-vars expr) (let-body expr))
(cons (cons 'list (let-inits expr))
'()))))
另一個(簡單)的選項是:
(define (let->combination expr)
(list 'apply
(make-lambda (let-vars expr) (let-body expr))
(cons 'list (let-inits expr))))
這是正確的形式:
((lambda (var1 ... varn) body) exp1 ... expn)
我們只是在調用帶有許多參數的函數,例如:
((lambda (var1 var2 var3) (+ var1 var2 var3)) 1 2 3)
=> 6
另一種形式將引發錯誤,因為解釋器認為您正在嘗試將1
用作函數:
((lambda (var1 var2 var3) (+ var1 var2 var3)) (1 2 3))
=> expected a procedure that can be applied to arguments
也許您對let
工作的方式感到困惑? 例如,此表達式:
(let ((x 1)
(y 2))
(+ x y))
只是語法糖,它將轉換為以下等效表達式:
((lambda (x y)
(+ x y))
1 2)
上面是一個過程應用程序,評估人員可以理解並很好地進行評估。 如SICP中所述, let
表達式的評估需要兩個步驟:首先,將表達式轉換為lambda
應用程序, 然后將所得轉換傳遞給評估器,以進行常規評估。 因此,不,您不應該在轉換內部添加一個apply
,讓評估程序中的現有代碼來處理這一問題,您只需發出一個評估程序可以理解的表達式即可。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.