我用引号定义一个列表。然后,我尝试使用通过cdr和car获得的此列表的元素来定义lambda操作。但是定义的lambda操作会给出有关其参数数量的错误。 错误消息是:
;过程#[compound-procedure XX]已使用1个参数调用;它只需要两个参数。
(define x '(lambda (n) (+ n 1)))
(cadr x) ;mit scheme interpreter displays (n)
(caddr x) ; this results in (+ n 1)
((lambda (cadr x)(caddr e)) 2) ; this is the problematic part which results in an error.
解决方案:创建一个临时环境,并在该环境中绑定lambda表达式的形式参数和实际参数,并使用该环境解释lambda表达式的主体。
答案 0 :(得分:1)
操作时:
(define x '(lambda (n) (+ n 1)))
您正在使绑定x
指向列表结构(lambda (n) (+ n 1))
。它与lambda
这样的表单无关:
(define x2 (lambda (n) (+ n 1)))
您可以在此处应用(x2 1) ; ==> 2
,因为它的值是闭包/过程/函数,因为它计算了lambda形式。
(lambda (cadr x)(caddr e))
不评估(cadr x)
,而是使用正式参数cadr
和x
创建一个闭包,以便您可以将结果((lambda (cadr x) ...) 1 2)
应用于在闭包中对cadr
的求值变成1
,而x
变成2
。对(caddr e)
的求值是在您申请时发生的,因此,如果调用((lambda (cadr x)(caddr e)) 'ignored1 'ignored2)
,它将与在创建闭包的环境中对(caddr e)
求值相同。 (eval `(lambda ,(cadr x) ,(caddr e)))
无法正常工作,因为您将主机与来宾混合在一起,将无法处理自由变量。
由于您要创建解释器,因此用户定义的过程将是数据结构,并且您的apply
将知道如何使用它。对表单的求值应该返回可以被标识为闭包的内容,并且您不能在解释器中使用任何其他代码来欺骗它,它引用了求值位置的词法范围以及{{1 }}如果是lambda。
我的一个这样做:
cdr
因此,评估lambda (define closure-tag (list 'closure)) ; make something that is not `eq?` with enything else
(define (closure? expr)
(and (pair? expr)
(eq? closure-tag (car expr))))
(define (lambda->closure expr env)
`(,closure-tag ,env ,@(cdr expr)))
成为(lambda (n) (+ n 1))
并申请((closure) ((#t . #t) ...) (n) (+ n 1))
时将使用环境((lambda (n) (+ n 1)) 2)
评估(+ n 1)
。结构的选择无关紧要,因为结构是lambda表单的评估与您的申请之间的协议。
您可以使((n . 2) (#t . #t) ...)
表单成为过程,但是它仍然不是来宾源的宿主版本,而是某种优化。我最近的lambda
之一这样做,并且总是接受2个参数。参数列表未评估且环境未评估。在eval
术语中,原始语种是用eval
和evlis
来修饰的。您所做的大多数设计选择都会有优点也有缺点,因此很有趣。
答案 1 :(得分:-2)
您需要使用EVAL将x的引用代码转换为实际函数:
((eval x) 2)
或
((eval `(lambda ,(cadr x) ,(caddr x))) 2)