簡體   English   中英

SICP練習4.6:在評估程序中實現let作為基於lambda的派生表達式

[英]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)

我發現該問題的大多數解決方案似乎都是錯誤的,有人可以確認是這種情況嗎?

SICP的此解決方案為例Wiki

 ;; 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM