2

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

4

2 回答 2

6

Common Lisp 使用 let 来绑定词法和“特殊”(动态)变量。您预期的行为是词汇的,而您观察到的行为是动态的。打印机变量都是特殊的,所以让我们为它们创建一个动态绑定。

打印机变量有时用于说明为什么动态绑定有用的示例。例如,您可以通过绑定 *print-base* 来控制 princ 的行为这一事实是通过动态绑定启用的,否则 princ 将在定义 princ 时引用 *print-base* active 的绑定。

这种行为是许多 Common Lisp 程序员坚持使用 *earmuffs* 特殊变量命名约定的主要原因。请注意,defvar 和 defparameter 都创建特殊变量。

于 2013-07-28T05:52:36.913 回答
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

于 2013-07-28T04:55:46.730 回答