0

我使用引号定义一个列表。然后,我尝试使用通过 cdr 和 car 获得的此列表的元素来定义 lambda 操作。但是定义的 lambda 操作给出了关于其参数数量的错误。错误信息是:

; 过程 #[compound-procedure XX] 已用 1 个参数调用;它恰好需要 2 个参数。

(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 表达式的主体。

4

2 回答 2

2

当你这样做时:

(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)求值变为和变为。的评估发生在您申请时,因此如果您调用它将返回与在创建闭包的环境中评估相同的结果。由于您将主机与客人混合在一起,因此您将无法处理自由变量,因此无法开始工作。cadr1x2(caddr e)((lambda (cadr x)(caddr e)) 'ignored1 'ignored2)(caddr e)(eval `(lambda ,(cadr x) ,(caddr e)))

由于您正在制作解释器,因此您的用户定义过程将是数据结构,您apply将知道如何处理它。表单的评估应该返回可以识别为闭包的东西,并且您不能在解释器中执行任何其他代码来欺骗它,引用它被评估的地方的词法范围以及cdrif 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(lambda (n) (+ n 1))变为((closure) ((#t . #t) ...) (n) (+ n 1))并申请((lambda (n) (+ n 1)) 2)(+ n 1)与 environment 一起评估((n . 2) (#t . #t) ...)。结构的选择无关紧要,因为结构是 lambda 形式的评估和您的应用之间的协议。

您可以使lambda表单成为程序,但它仍然不是来宾源的主机版本,而是某种优化。我最近的一个是eval这样做的,并且总是接受 2 个论点。参数列表未评估和环境。用eval行话来说,基元是用evlis和咖喱化的apply。您所做的大多数设计选择既有优点也有缺点,而且玩起来很有趣。

于 2019-05-08T21:30:34.093 回答
-2

您需要使用 EVAL 将 x 的引用代码转换为实际函数:

((eval x) 2)

或者

((eval `(lambda ,(cadr x) ,(caddr x))) 2)
于 2019-05-08T20:01:48.707 回答