SICP练习4.6:在评估程序中实现let作为基于lambda的派生表达式

Him*_*rzi 1 lisp evaluation scheme functional-programming sicp

我正在尝试编写一个转换let形式表达式的函数:

(let ((var1 exp1) ... (varn expn))
  body)
Run Code Online (Sandbox Code Playgroud)

转换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))) 
Run Code Online (Sandbox Code Playgroud)

我相信let->combination会返回这种形式的清单:

((lambda (var1 ... varn) body) (exp1 ... expn))
Run Code Online (Sandbox Code Playgroud)

但是,我认为它应该返回的是以下形式的列表:

((lambda (var1 ... varn) body) exp1 ... expn)

以某种方式,不应在中使用对apply函数的调用let->combination吗?应该如何let->combination修改以包含apply

Jos*_*lor 5

相信 let-combination将返回这种形式的列表:

((lambda (var1 ... varn) body) (exp1 ... expn))
Run Code Online (Sandbox Code Playgroud)

但是,我认为它应该返回的是以下形式的列表:

((lambda (var1 ... varn)    body)  exp1 ... expn)
Run Code Online (Sandbox Code Playgroud)

有好消息,有坏消息。坏消息是你的信念是错误的。好消息是您的想法是正确的。也就是说,您确实需要结果((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))) 
Run Code Online (Sandbox Code Playgroud)

那么(let->combination '(let ((x 34)) (list x))),例如,会发生什么?您将评估:

(cons (make-lambda (let-vars expr) (let-body expr)) 
      (let-inits expr))
Run Code Online (Sandbox Code Playgroud)

这些零件会产生什么? (make-lambda …)创建您期望的lambda表达式:

(lambda (x) (list x))
Run Code Online (Sandbox Code Playgroud)

(let-inits …)返回:

(34)
Run Code Online (Sandbox Code Playgroud)

现在的问题是,(cons '(lambda (x) (list x))'(34))'是什么?您可以轻松地进行测试;它的:

((lambda (x) (list x)) 34)
Run Code Online (Sandbox Code Playgroud)

这可能是对工作方式有些困惑的结果cons。列表可以是:空列表,'()或由cons生成的一对,其car是列表的元素,而cdr是列表的其余部分。从而:

               (cons 1 '())  ;=> (1)
               (cons 1 '(2)) ;=> (1 2)
(cons '(lambda (x) x) '(34)) ;=> ((lambda (x) x) 34)
Run Code Online (Sandbox Code Playgroud)

我相信let-combination将返回这种形式的列表:

((lambda (var1 ... varn) body) (exp1 ... expn))
Run Code Online (Sandbox Code Playgroud)

…以某种方式,在let组合中不应该使用对apply函数的调用吗?应该如何修改let-combination以使其适用?

如我们所见,代码实际上产生了您想要的结果。如果它确实产生了类似的东西((lambda (var1 ... varn) body) (exp1 ... expn)),那么您可能需要使用apply。最简单的方法是生产

(apply (lambda (var1 ... varn) body) (list exp1 ... expn))
Run Code Online (Sandbox Code Playgroud)

请注意,我们已添加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))
                     '()))))
Run Code Online (Sandbox Code Playgroud)

另一个(简单)的选项是:

(define (let->combination expr) 
   (list 'apply
         (make-lambda (let-vars expr) (let-body expr)) 
         (cons 'list (let-inits expr))))
Run Code Online (Sandbox Code Playgroud)