免责声明:我不知道 Emacs Lisp,但我知道 Lisp。
我相信您遇到的是与非驻留符号的读取/打印混淆。也就是说,我怀疑 Emacs Lisp 中的(make-symbol ...) 就像 Common Lisp 等其他方言中的同名函数一样,创建了一个新的符号对象,该对象与扫描的同名符号无关来自打印的符号(来自文件、终端、字符串、编辑缓冲区……)。
当您获取代码生成函数(也称为 S 表达式)的打印输出时,您的代码就可以工作,因为符号 four 的打印符号被读回 Lisp 并被保留,导致该符号成为与函数名称相同的对象four。
但是(make-symbol "four") 是一个不同的符号对象。当您在函数的代码上使用eval 时,您是在直接使用数据结构,因此您的错误不会通过将代码简化为文本并将其读回来掩盖。该符号不会通过实习转换为标记 four 并返回到对象 four。 eval 将看到来自make-symbol 的原始符号:指向同一块内存的同一机器指针。
(在 Common Lisp 中,来自 (make-symbol "four") 的“uninterned symbol”通常用散列点表示法打印,例如 #:four 以便您可以发现它们。(实际上 #: 表示没有 home 包的符号,不是没有实习,但这是非常模糊的 Common Lisp 微妙之处。))
无论如何,寻找一个名为intern 的函数。 (intern "four") 将查找具有该名称的现有符号并将其返回,而不是创建一个新符号。
;; two symbol interns for same name result in the same object
;; we are comparing the same pointer to itself
(eq (intern "foo") (intern "foo")) -> t
;; two symbol constructions result in two different object
;; two different pointers to separately allocated objects
(eq (make-symbol "foo") (make-symbol "foo")) -> nil
还有:
如果你想编写代码生成代码,你真的必须学习反引号。否则,您将采用 1960 年代的方式,而不是现代 1970 年代的方式:
;; don't let your friends do this:
(defun makeplusser (x)
(list 'defun (make-symbol (format "%s+" x)) '(y)
(list '+ (list x) 'y)))
;; teach them this:
(defun makeplusser (x)
`(defun ,(intern (format "%s+" x)) (y)
(+ (,x) y)))
;; even more clearly, perhaps
(defun makeplusser (func-to-call)
(let ((func-name (format "%s+" x)))
`(defun ,func-name (arg)
(+ (,func-to-call) arg))))
何时使用make-symbol
当您需要创建一个保证唯一的符号(与任何其他符号不同的对象)时,请使用make-symbol,即使它恰好与其他符号同名。其他 Lisp 方言也有一个名为 gensym 的函数,它类似于 make-symbol,但它还在名称上附加了一个递增的数字标记,以便在同一上下文中出现多个“gensyms”时更容易区分这些“gensyms”。 gensym 在拥有它的 Lisp 中比 make-symbol 更常用。唯一符号在代码生成(宏)中很有用,用于为周围代码绝对不可见的事物生成唯一标签,例如插入的代码块中的临时局部变量。你的函数的参数应该是一个唯一的符号:
;; even more clearly, perhaps
(defun makeplusser (func-to-call)
(let ((func-name (format "%s+" x))
(arg-sym (make-symbol "arg"))
`(defun ,func-name (,arg)
(+ (,func-to-call) ,arg))))
原因是:Emacs Lisp 是动态作用域的。如果我们调用参数y,则存在被调用的用户函数(如(four))可能包含对y 的引用的风险,程序员的意图是访问他或她自己的变量y。但是您生成的函数意外捕获了引用!
通过使用 gensym 作为参数,我们避免了这个问题;用户的代码不可能引用参数:我们已经实现了“卫生”或“透明”。