无法说明哪些语言具有 GENSYM 的等价物。许多语言没有一流的符号数据类型(具有内部和非内部符号),并且许多语言没有提供类似的代码生成(宏,...)工具。
一个interned symbol注册在一个package中。 uninterned 不是。如果阅读器(阅读器是 Lisp 子系统,它将文本 s 表达式作为输入并返回数据)在同一个包中看到两个同名的内部符号,它假定它是相同的符号:
CL-USER 35 > (eq 'cl:list 'cl:list)
T
如果读者看到了一个未执行的符号,它会创建一个新符号:
CL-USER 36 > (eq '#:list '#:list)
NIL
Uninterned 符号在名称前写有#:。
GENSYM 在 Lisp 中用于创建 编号 非驻留符号,因为它有时在代码生成和调试代码中很有用。请注意,这些符号始终是新的,而不是 eq 对其他任何东西。但符号名称可能与另一个符号的名称相同。该数字为人类读者提供了有关身份的线索。
使用MAKE-SYMBOL的示例
make-symbol 使用字符串参数作为名称创建一个新的 uninterned 符号。
让我们看看这个函数生成一些代码:
CL-USER 31 > (defun make-tagbody (exp test)
(let ((start-symbol (make-symbol "start"))
(exit-symbol (make-symbol "exit")))
`(tagbody ,start-symbol
,exp
(if ,test
(go ,start-symbol)
(go ,exit-symbol))
,exit-symbol)))
MAKE-TAGBODY
CL-USER 32 > (pprint (make-tagbody '(incf i) '(< i 10)))
(TAGBODY
#:|start| (INCF I)
(IF (< I 10) (GO #:|start|) (GO #:|exit|))
#:|exit|)
上面生成的代码使用了非内部符号。 #:|start| 实际上是同一个符号。如果我们有*print-circle* 到T,我们会看到这一点,因为打印机会清楚地标记相同的对象。但是在这里我们没有得到这个附加信息。现在,如果您嵌套此代码,那么您会看到不止一个 start 和一个 exit 符号,每个符号都在两个地方使用。
使用GENSYM的示例
现在让我们使用gensym。 Gensym 还创建了一个不受限制的符号。可选地,此符号由字符串命名。添加了一个数字(参见变量CL:*GENSYM-COUNTER*)。
CL-USER 33 > (defun make-tagbody (exp test)
(let ((start-symbol (gensym "start"))
(exit-symbol (gensym "exit")))
`(tagbody ,start-symbol
,exp
(if ,test
(go ,start-symbol)
(go ,exit-symbol))
,exit-symbol)))
MAKE-TAGBODY
CL-USER 34 > (pprint (make-tagbody '(incf i) '(< i 10)))
(TAGBODY
#:|start213051| (INCF I)
(IF (< I 10) (GO #:|start213051|) (GO #:|exit213052|))
#:|exit213052|)
现在这个数字表明这两个未留存的#:|start213051| 符号实际上是相同的。当代码被嵌套时,新版本的开始符号将有一个不同的数字:
CL-USER 7 > (pprint (make-tagbody `(progn
(incf i)
(setf j 0)
,(make-tagbody '(incf ij) '(< j 10)))
'(< i 10)))
(TAGBODY
#:|start2756| (PROGN
(INCF I)
(SETF J 0)
(TAGBODY
#:|start2754| (INCF IJ)
(IF (< J 10)
(GO #:|start2754|)
(GO #:|exit2755|))
#:|exit2755|))
(IF (< I 10) (GO #:|start2756|) (GO #:|exit2757|))
#:|exit2757|)
因此它有助于理解生成的代码,而无需打开*print-circle*,这将标记相同的对象:
CL-USER 8 > (let ((*print-circle* t))
(pprint (make-tagbody `(progn
(incf i)
(setf j 0)
,(make-tagbody '(incf ij) '(< j 10)))
'(< i 10))))
(TAGBODY
#3=#:|start1303| (PROGN
(INCF I)
(SETF J 0)
(TAGBODY
#1=#:|start1301| (INCF IJ)
(IF (< J 10) (GO #1#) (GO #2=#:|exit1302|))
#2#))
(IF (< I 10) (GO #3#) (GO #4=#:|exit1304|))
#4#)
Lisp reader(读取文本表示的 s-expressions 的子系统)可以阅读以上内容,但对于人类阅读者来说则略逊一筹。