SBCL更改本地绑定函数对象的EQness,即使它未设置?

Rai*_*wig 7 sbcl common-lisp ansi-common-lisp

给出这个示例代码(来自Reddit/r/lisp问题):

(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*)))
Run Code Online (Sandbox Code Playgroud)

以上建立了一个功能NEXT.在里面LET,我们保留旧功能OLD-NEXT.然后我们重新定义了NEXT内部的全局函数LET.

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

? (load "test.lisp")

 1 EQ? T
 2 EQ? T
Run Code Online (Sandbox Code Playgroud)

只有SBCL(SBCL 1.3.11)有不同的结果:

* (load "test.lisp")

 1 EQ? T
 2 EQ? NIL
Run Code Online (Sandbox Code Playgroud)

局部变量的值old-next不再eq是全局变量的值*next*.

为什么???

jki*_*ski 2

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

(defun foobar ()
  (print :foo))

(let ((old #'foobar))
  (funcall old)
  (defun foobar ()
    (print :bar))
  (funcall old))
Run Code Online (Sandbox Code Playgroud)

印刷

:FOO 
:BAR 
Run Code Online (Sandbox Code Playgroud)

但如果你使用SETF变量,

(defun foobar ()
  (print :foo))

(let ((old #'foobar))
  (funcall old)
  (setf old #'foobar)
  (defun foobar ()
    (print :bar))
  (funcall old))
Run Code Online (Sandbox Code Playgroud)

它打印

:FOO 
:FOO 
Run Code Online (Sandbox Code Playgroud)