为什么lisp使用gensym和其他语言don';T 如果我错了,请纠正我,但是在爪哇、C、C++、Python、JavaScript或者我使用过的任何其他语言中,没有什么类似GySym的,而且我似乎从来都不需要它。为什么它在Lisp中是必需的而在其他语言中不是必需的?为了澄清,我正在学习Common Lisp。
无法说出哪些语言与为什么lisp使用gensym和其他语言don';T 如果我错了,请纠正我,但是在爪哇、C、C++、Python、JavaScript或者我使用过的任何其他语言中,没有什么类似GySym的,而且我似乎从来都不需要它。为什么它在Lisp中是必需的而在其他语言中不是必需的?为了澄清,我正在学习Common Lisp。,lisp,common-lisp,Lisp,Common Lisp,无法说出哪些语言与GENSYM等效。许多语言都没有一流的符号数据类型(包含内部和非内部符号),许多语言也没有提供类似的代码生成(宏…)功能 在包中注册一个内部符号。一个不感兴趣的人不是。如果读取器(读取器是以文本s表达式作为输入并返回数据的Lisp子系统)在同一个包中看到两个具有相同名称的插入符号,则假定它是相同的符号: CL-USER 35 > (eq 'cl:list 'cl:list) T 如果读取器看到一个非预期符号,它将创建一个新符号: CL-USER 36 > (eq
GENSYM
等效。许多语言都没有一流的符号数据类型(包含内部和非内部符号),许多语言也没有提供类似的代码生成(宏…)功能
在包中注册一个内部符号。一个不感兴趣的人不是。如果读取器(读取器是以文本s表达式作为输入并返回数据的Lisp子系统)在同一个包中看到两个具有相同名称的插入符号,则假定它是相同的符号:
CL-USER 35 > (eq 'cl:list 'cl:list)
T
如果读取器看到一个非预期符号,它将创建一个新符号:
CL-USER 36 > (eq '#:list '#:list)
NIL
不需要的符号写在名称前面
GENSYM
在Lisp中用于创建编号的不需要的符号,因为它有时在代码生成和调试代码时很有用。请注意,符号始终是新的,而不是任何其他符号的eq
。但符号名称可能与另一个符号的名称相同。这个数字为人类读者提供了关于身份的线索
使用MAKE-SYMBOL的示例
make symbol
使用字符串参数作为名称创建新的不需要的符号
让我们看看这个函数生成的一些代码:
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|)
现在,这个数字表示两个不相关的:| 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|)
以上内容对于Lisp阅读器(读取s表达式以进行文本表示的子系统)来说是可读的,但对于人类阅读器来说则稍差一些。我认为(在Lisp意义上)在语言中最有用(在这些语言中,语言的语法可以表示为该语言的数据)
java、C++、Python、JavaScript都不是同质的。
一旦有了符号,就需要某种方法来动态创建它们
gensym
是一种可能性,但您也可以使用它们
顺便说一句,是一种类似lisp的方言,它不使用gensym
或通过插入字符串创建符号,而是使用。(实际上,熔化符号是预定义的类符号的实例。Common Lisp有一个强大的宏系统。您可以创建新的语法模式,使其完全按照您希望的方式运行。它甚至用自己的语言表达,使得语言中的一切都可以用来将代码从您想要编写的内容转换为CL真正理解的内容。所有具有强大宏系统的语言都在其宏实现中隐式地提供或执行该功能
在Common Lisp中,如果要生成符号不应与结果中任何其他位置使用的元素匹配的代码,可以使用gensym
。没有它,就不能保证用户使用宏实现者也使用的符号,他们开始干预,结果与预期行为不同。它确保同一宏的嵌套扩展不会干扰以前的扩展。使用通用的Lisp宏系统,可以使更严格的宏系统类似于Scheme和
在该方案中,有几个宏观系统。具有模式匹配的一种,其中新引入的符号会自动起作用,就像它们是由gensym
生成的一样<默认情况下,code>syntax case
也会生成新符号,就好像它们是用gensym
生成的一样,还有一种方法可以减少卫生问题。您可以使用语法大小写
生成CLdefmacro
,但由于Scheme没有gensym
,因此无法使用它生成宏
java、C++、C++、python、javascript都是,它们都没有简单的模板宏。因此,他们没有
gensym
,因为他们不需要它。因为在这些语言中引入新语法的唯一方法就是希望它的下一个版本能够提供它
有两种Algol方言会出现强大的宏。和。它们都有卫生方法,这意味着引入的变量的行为就好像它们是用gensym
制造的一样
在CL、Scheme、Nemerle、Perl6中,您不需要等待语言特性。你可以自己做!Java和PHP中的新闻都可以很容易地用其中任何一个宏实现,如果它还不可用的话。
gensym
在大多数Prolog解释器中作为谓词提供。您可以在eponym库中找到它。Gensym与您在Java中使用newsymbol(“foo”)时所做的工作没有什么不同代码>。符号的实习实际上更有趣(但一点也不独特)。Intern就像一个符号工厂方法,如果你之前创建了一个类似的符号,你可以得到相同的符号;gensym就像一个构造器,你总是能得到一些新的东西。实际上,有些东西是坏的。有关详细信息,请参阅在Linux内核的\uuuuuu UNIQUE\u ID
等情况下使用的GCC的\uuuu计数器。然而,一般来说,即使是更理智的gensym也不太可能是一个好主意,因为在语言规则变得正式不合理之前,依赖于符号解析过程交互的方法对语言的底层语义太过干扰。在需要时,卫生也很难保持。在Javascript中,您可以使用“var”定义一个新变量,并将其限制在最严格的词法范围内。之所以使用Gensym是因为CL中没有类似“var”的东西吗?@michaelAdam Javascript没有强大的宏。例如,执行cond
so t
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|)
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#)