Common Lisp闭包捕获特殊变量遇阻,求无全局污染解决方案
Common Lisp动态变量闭包问题的解决方案
第二个代码报错的核心是动态变量(special标记)的作用域是动态的,而非词法的。当你在let块里声明a为特殊变量并返回lambda时,let创建的动态环境在离开块后就消失了。后续调用这个lambda时,没有任何动态绑定的a存在,自然会报“变量未绑定”。第一个代码能运行,是因为lambda的调用完全在let的动态环境内部,此时a的动态绑定依然有效。
以下是几种不污染全局命名空间的解决方法:
方法1:词法捕获值,在闭包内重建动态绑定
把a的值用词法变量保存,在闭包调用时重新建立a的动态绑定:
(funcall (let ((a-val 2)) ; 词法变量捕获值,不声明special (lambda (x) (let ((a a-val)) (declare (special a)) ; 在当前动态环境绑定special变量a (funcall (lambda (x) (declare (special a)) (+ a (* x 3))) x)))) 3)
运行结果:11
方法2:避免不必要的动态变量,直接用词法捕获
如果不需要动态作用域的特性(比如动态重绑定),完全可以去掉special声明,让闭包直接词法捕获a:
(funcall (let ((a 2)) (lambda (x) (funcall (lambda (x) (+ a (* x 3))) x))) 3)
这样代码更简洁,同样得到11,且不会污染全局命名空间。
方法3:将动态绑定与闭包调用放在同一环境
如果必须保留动态作用域,可以把闭包的调用放在a的动态绑定环境内:
(let ((a 2)) (declare (special a)) (funcall (lambda (x) (funcall (lambda (x) (declare (special a)) (+ a (* x 3))) x)) 3))
这种方式确保调用闭包时a的动态绑定存在,运行结果同样为11。
内容的提问来源于stack exchange,提问作者Orm Finnendahl
相关产品推荐
相关产品推荐

