Common Lisp 和 Scheme 词法闭包的区别
Posted
技术标签:
【中文标题】Common Lisp 和 Scheme 词法闭包的区别【英文标题】:Differences between Common Lisp and Scheme lexical closures 【发布时间】:2013-06-24 07:26:51 【问题描述】:在 Common Lisp 中,我可以评估以下 sn-p 代码(在 SBCL 中),而不会收到任何语法错误的信号:
(let ((x 0))
(defun my-incf (y)
(setf x (+ x y)))
(defun my-decf (y)
(setf x (- x y))))
MY-DECF
CL-USER> (my-incf 1)
1
CL-USER> (my-incf 1)
2
CL-USER> (my-decf 1)
1
CL-USER> (my-decf 1)
0
当我尝试评估相应的 Scheme sn-p 代码时(在 DrRacket 中):
(let ((x 0))
(define (my-incf y)
(set! x (+ x y)))
(define (my-decf y)
(set! x (- x y))))
它表示语法错误。
begin (possibly implicit): no expression after a sequence of internal definitions in: (begin (define (my-incf y) (set! x (+ x y))) (define (my-decf y) (set! x (- x y))))
有人知道为什么这不能在Scheme中完成吗?
【问题讨论】:
您需要查看 DrRacket 中 LET 的语法定义。 Common Lisp 示例是有效的,但我不会在我的代码中使用它。 我会改用 CLOS。使用上述方法会使调试更加困难,并且会阻止 DEFUN 形式被编译器识别为函数定义。在 LET 中,DEFUN 不再是***表单。 不是很明显吗? X 是 CLOS 实例中的一个插槽。那么函数就是方法。 顺便说一句,关于你正在做的事情与你可以用 CLOS 做的事情的相似性,Scheme 最初是作为一种探索面向对象思想的语言而发明的——大概使用类似于那些如下所述。 我不一定会使用 OOP 来解决具有全局变量的模块的简单问题。函数不是***表单的问题可以通过(defvar *counter* 0)
解决,函数参考*counter*
。如果模块尖叫“我真的应该是一个支持多个实例化的对象,并且所有这些全局变量都应该是我的实例变量”,则适用 OOP 方法。如果您没有预料到需要多次实例化,或者没有任何继承等好处,请坚持使用全局变量。动态变量是执行全局变量的“Common Lispy”方式。
【参考方案1】:
您不能在Scheme 中定义***之外的***绑定。 (而let
的内部肯定在顶层之外——您所拥有的是内部定义,它们不会导出到顶层。)但是,使用define-values
,您仍然可以做你需要做的:
(define-values (my-incf my-decf)
(let ((x 0))
(values (lambda (y)
(set! x (+ x y))
x)
(lambda (y)
(set! x (- x y))
x))))
但是,您仍然可以使用内部定义,以使您的代码更具可读性:
(define-values (my-incf my-decf)
(let ((x 0))
(define (my-incf y)
(set! x (+ x y))
x)
(define (my-decf y)
(set! x (- x y))
x)
(values my-incf my-decf)))
两全其美。 :-) 在这种情况下,values
将内部my-incf
和my-decf
定义发送到外部define-values
,这是真正的***定义发生的地方。
【讨论】:
作为老师曾经对我说过:“Scheme 是现代的 Lisp”。【参考方案2】:在这种情况下我会使用 Chris 的解决方案,但如果您增加对 x 的操作数量,这里还有一个本着“让 Lambda 工作”精神的解决方案:
(define inc-dec
(let ((x 0))
(lambda (msg y)
(case msg
((incf) (set! x (+ x y)))
((decf) (set! x (- x y)))
(else (error "wot?")))
x)))
(inc-dec 'incf 1)
(inc-dec 'incf 1)
(inc-dec 'decf 1)
(inc-dec 'decf 1)
【讨论】:
【参考方案3】:不太优雅,但非常简单:定义***变量,然后将 set!
或 setf
从 let
内部转换为 lambdas,具体取决于它是 Scheme 还是 CL。
【讨论】:
以上是关于Common Lisp 和 Scheme 词法闭包的区别的主要内容,如果未能解决你的问题,请参考以下文章
Common Lisp : Lexical varible , Dynamic varible ——作用域,生存期 ——environment : 绑定, 闭包与共享对象