在 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)
而是创建一个带有形式参数 cadr
和 x
的闭包,以便您可以应用结果 ((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
行话中,原语是用 evlis
和 apply
柯里化的。您所做的大多数设计选择都有优点和缺点,而且玩起来很有趣。
我用引号定义了一个列表。然后,我尝试使用通过 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)
而是创建一个带有形式参数 cadr
和 x
的闭包,以便您可以应用结果 ((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
行话中,原语是用 evlis
和 apply
柯里化的。您所做的大多数设计选择都有优点和缺点,而且玩起来很有趣。