为什么ANSI Common Lisp的这个范围示例不能按预期工作?

时间:2013-07-28 03:36:19

标签: common-lisp scoping

我是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*变量。

2 个答案:

答案 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的定义有关,所以它是8f1正在定义但不是它正在执行