9

Given this example code (from a Reddit /r/lisp question):

(defun next (pos)
  (nth (1+ pos)
       '(0 1 2 3 4 5 6 7 8 9 10)))

(defvar *next* (function next))

(let ((old-next #'next)
      (previous (make-hash-table)))
  (format t "~% 1 EQ? ~a" (eq old-next *next*))
  (defun next (pos)
    (or (gethash pos previous)
        (setf (gethash pos previous) (funcall old-next pos))))
  (format t "~% 2 EQ? ~a" (eq old-next *next*)))

Above establishes a function NEXT. Inside the LET, we keep the old function in OLD-NEXT. Then we redefine the global function NEXT inside the LET.

CCL/CMUCL/GCL/ECL/CLISP/LispWorks/ABCL:

? (load "test.lisp")

 1 EQ? T
 2 EQ? T

Only SBCL (SBCL 1.3.11) has a different result:

* (load "test.lisp")

 1 EQ? T
 2 EQ? NIL

The value of the local variable old-next is no longer eq to the value of the global variable *next*.

Why???

4

2 回答 2

3

无论变量是否特殊,行为都会发生变化:

(inspect
  (progn
    (defun next ())
    (let ((old #'next)
          (foo #'next))
      (declare (special foo))
      (defun next () :different)
      (list old foo))))

The object is a proper list of length 2.
0. 0: #<FUNCTION NEXT>
1. 1: #<FUNCTION NEXT {10037694EB}>

第一个元素指的是最新的定义,而第二个元素是从特殊变量中获得的,正如预期的那样,是旧的定义。当您删除特殊声明时,两个引用都是 EQ(并指向新定义)。

于 2016-12-26T14:08:19.980 回答
2

看起来 SBCL 正试图变得聪明并优化变量。

(defun foobar ()
  (print :foo))

(let ((old #'foobar))
  (funcall old)
  (defun foobar ()
    (print :bar))
  (funcall old))

印刷

:FOO 
:BAR 

但是如果你SETF在变量上使用,

(defun foobar ()
  (print :foo))

(let ((old #'foobar))
  (funcall old)
  (setf old #'foobar)
  (defun foobar ()
    (print :bar))
  (funcall old))

它打印

:FOO 
:FOO 
于 2016-12-26T11:40:00.417 回答