我是lisp的新手,并通过 Paul Graham 浏览 ANSI Common Lisp ,其中一个练习是定义一个类似apply的函数,其中任何数字都打印出来之前它返回将默认以八进制打印。
我尝试了以下内容:
(let ((*print-base* 8))
(defun like-apply (&rest args)
(apply #'apply args)))
但它没有按预期工作:
(like-apply #'princ '(8)); returns 8 8 (expecting 10 8)
以下情况有效:
(defun apply8 (&rest args)
(let ((*print-base* 8))
(apply #'apply args)))
正确返回:
(apply8 #'princ '(8)); returns 10 8 (as expected)
所以我的问题是为什么第二个例子有效,但不是第一个?两者似乎都在操纵*print-base*
变量。
答案 0 :(得分:6)
Common Lisp使用let来绑定词法和“特殊”(动态)变量。你期望的行为是词汇,你观察到的行为是动态的。打印机变量都很特殊,因此我们为它们创建一个动态绑定。
有时会在动态绑定有用的示例中使用打印机变量。例如,您可以通过绑定* print-base *控制princ的行为这一事实是通过动态绑定启用的,否则在定义princ时,princ将引用* print-base * active的绑定。
这种行为是许多Common Lisp程序员坚持特殊变量的* earmuffs *命名约定的主要原因。请注意,defvar和defparameter都会创建特殊变量。
答案 1 :(得分:4)
您观察到的行为是正确的。
比较
将具有指导意义(let ((*print-base* 8))
(defun f1 ()
*print-base*))
带
(defun f2 ()
(let ((*print-base* 8))
*print-base*))
发现(f1)
返回10
而(f2)
返回8
。
这是因为*print-base*
与f1
的定义有关,所以它是8
而f1
正在定义但不是它正在执行。