在 Scheme 中使用带有 cdr 和 car 的 lambda

Using lambda with cdr and car in Scheme

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

;The procedure #[compound-procedure XX] has been called with 1 argument; it requires exactly 2 arguments.

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

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

((eval x) 2)

((eval `(lambda ,(cadr x) ,(caddr x))) 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) 而是创建一个带有形式参数 cadrx 的闭包,以便您可以应用结果 ((lambda (cadr x) ...) 1 2) 这样在闭包中计算 cadr 变成 1 并且 x 变成 2(caddr e) 的评估发生在您应用时,因此如果您调用 ((lambda (cadr x)(caddr e)) 'ignored1 'ignored2) 它将 return 与在创建闭包的环境中评估 (caddr e) 相同。使 (eval `(lambda ,(cadr x) ,(caddr e))) 正常工作是不可能的,因为您将无法处理自由变量,因为您将主机与来宾混在一起。

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

您可以使 lambda 表单成为程序,但它仍然不是来宾源的主机版本,而是某种优化。我最近的一个 eval 就是这样做的,并且总是接受 2 个参数。未评估的参数列表和环境。在 eval 行话中,原语是用 evlisapply 柯里化的。您所做的大多数设计选择都有优点和缺点,而且玩起来很有趣。