12

关于Common lisp,我有些不明白。

假设我正在编写一个类似的宏:

(defmacro test-macro () 
   (let ((result (gensym))) 
      `(let ((,result 1))
         (print (incf ,result))))) 

比我能做的

> (test-macro)
2
2

现在我想看看它是如何扩展的

> (macroexpand-1 '(test-macro))
(LET ((#:G4315 1)) (PRINT (INCF #:G4315))) ;
T

行。gensym 生成了一些独特的符号,这些符号被打印为 uninterned。

据我所知,uninterned 符号是评估器不会在内部创建符号数据绑定的符号。

因此,如果我们将宏扩展为该表单,则 (incf #:G4315) 上应该会出现错误。为了测试这一点,我们可以在 REPL 中评估该表单:

> (LET ((#:G4315 1)) (PRINT (INCF #:G4315)))
*** - SETQ: variable #:G4315 has no value

那么为什么扩展为这个字符串的宏可以完美地工作而表单本身却不能呢?

4

2 回答 2

21

符号可以在包中也可以不被实习。可以查找并找到包含在包中的符号。无法在包中查找 uninterned 符号。一个包中只能有一个特定名称的符号。只有一个符号CL-USER::FRED

你写

据我所知,uninterned 符号是评估器不会在内部创建符号数据绑定的符号。

那是错误的。Uninterned Symbols 是没有在任何包中的符号。否则他们完全没问题。interned表示在包的注册表中为其符号注册。

s-expression阅读器在阅读过程中确实使用符号名称和包来识别符号。如果没有这样的符号,它就会被拘留。如果有一个,则返回这个。

读者确实在当前包中按名称查找符号:

 (read-from-string "FOO") -> symbol `FOO`

第二次:

 (read-from-string "FOO") -> symbol `FOO`

它始终是同一个符号FOO

 (eq (read-from-string "FOO") (read-from-string "FOO"))  -> T

#:FOO是名称为 的非内部符号的语法FOO。它没有在任何包中实习。如果读者看到这种语法,它会创建一个新的非内部符号。

 (read-from-string "#:FOO") -> new symbol `FOO`

第二次:

 (read-from-string "#:FOO") -> new symbol `FOO`

两个符号不同。它们具有相同的名称,但它们是不同的数据对象。除了包之外,没有其他符号注册表。

 (eq (read-from-string "#:FOO") (read-from-string "#:FOO"))  -> NIL

因此,在您的情况下(LET ((#:G4315 1)) (PRINT (INCF #:G4315))),未执行的符号是不同的对象。第二个是一个不同的变量。

Common Lisp 有一种打印数据的方法,以便在打印/读取期间保留身份

CL-USER 59 > (macroexpand-1 '(test-macro))
(LET ((#:G1996 1)) (PRINT (INCF #:G1996)))
T

CL-USER 60 > (setf *print-circle* t)
T

CL-USER 61 > (macroexpand-1 '(test-macro))
(LET ((#1=#:G1998 1)) (PRINT (INCF #1#)))
T

现在您看到打印的 s 表达式具有#1=第一个符号的标签。然后它稍后引用相同的变量。这可以被读回并保留符号身份 - 即使阅读器无法通过查看包装来识别符号。

因此,宏创建了一个表格,其中只生成了一个符号。当我们打印该表单并想要读回它时,我们需要确保保留未留存符号的身份。*print-circle*使用set to打印T有助于做到这一点。

问:为什么我们在宏中通过 using GENSYM( generate symbol ) 使用 uninterned 生成的符号?

这样我们就可以拥有独特的新符号,它们不会与代码中的其他符号发生冲突。他们通过函数获得名称gensym- 通常在末尾带有计数的数字。由于它们是新的符号,没有在任何包中实习,所以不会有任何命名冲突。

CL-USER 66 > (gensym)
#:G1999

CL-USER 67 > (gensym)
#:G2000

CL-USER 68 > (gensym "VAR")
#:VAR2001

CL-USER 69 > (gensym "PERSON")
#:PERSON2002

CL-USER 70 > (gensym)
#:G2003

CL-USER 71 > (describe *)

#:G2003 is a SYMBOL
NAME          "G2003"
VALUE         #<unbound value>
FUNCTION      #<unbound function>
PLIST         NIL
PACKAGE       NIL                      <------- no package
于 2012-12-21T09:35:30.877 回答
0

gensym生成一个符号,当您打印它时,您会得到该符号的“字符串”表示,它与“阅读器”表示不同,即符号的代码表示。

于 2012-12-21T09:35:04.093 回答