我编写了一个特殊的解析器生成器,它创建代码将旧的和鲜为人知的7位字符集转换为unicode.对解析器生成器的调用扩展为defun
包含在a中的一堆s progn
,然后进行编译.我只想将生成的defun
s中的一个 - 顶级的 - 暴露给系统的其余部分; 所有其他内容都是解析器的内部,只能从顶层的动态范围内调用.因此,defun
生成的其他s具有未处理的名称(用其创建gensym
).这个策略适用于SBCL,但我最近使用CLISP进行了第一次测试,我得到的错误如下:
*** - FUNCALL: undefined function #:G16985
似乎CLISP无法处理具有未分隔名称的函数.(有趣的是,系统编译没有问题.)编辑:在大多数情况下,它似乎可以处理具有未分隔名称的函数.请参阅下面的Rörd的答案.
我的问题是:这是CLISP的一个问题,还是Common Lisp的限制,某些实现(例如SBCL)碰巧被克服了?
编辑:
例如,顶级生成函数(被调用)的宏扩展parse
有一个如下表达式:
(PRINC (#:G75735 #:G75731 #:G75733 #:G75734) #:G75732)
评估此表达式(通过调用parse
)会导致类似上面的错误,即使该函数在同一个宏扩展中明确定义:
(DEFUN #:G75735 (#:G75742 #:G75743 #:G75744) (DECLARE (OPTIMIZE (DEBUG 2))) (DECLARE (LEXER #:G75742) (CONS #:G75743 #:G75744)) (MULTIPLE-VALUE-BIND (#:G75745 #:G75746) (POP-TOKEN #:G75742) ...
#:G75735的两个实例绝对是相同的符号 - 而不是两个具有相同名称的不同符号.正如我所说,这适用于SBCL,但不适用于CLISP.
编辑:
SO用户Joshua Taylor指出这是由于长期存在的CLISP错误.
你没有显示给出错误的一行,所以我只能猜测,但就我所见,唯一可能导致这个问题的是你指的是符号的名称,而不是试图称它时符号本身.
如果您指的是符号本身,那么您的所有lisp实现都必须查找该符号symbol-function
.是否实习并不重要.
请问为什么你没有考虑另一种隐藏函数的方法,即一个labels
语句或在一个只导出一个外部函数的新包中定义函数?
编辑:以下示例从与CLISP提示的交互中逐字复制.
如您所见,调用gensym命名的函数正在按预期工作.
[1]> (defmacro test () (let ((name (gensym))) `(progn (defun ,name () (format t "Hello!")) (,name)))) TEST [2]> (test) Hello! NIL
也许你试图调用函数的代码在之前被评估defun
?如果宏扩展中除了各种defun
s 之外还有任何代码,它可能是依赖于实现的,首先得到的是什么,因此SBCL和CLISP的行为可能会有所不同而没有任何违反标准的行为.
编辑2:一些进一步的调查表明,CLISP的行为取决于代码是直接解释还是首先编译然后解释.您可以通过load
在CLISP中直接使用Lisp文件或首先调用compile-file
它然后再load
使用FASL 来查看差异.
您可以通过查看CLISP提供的第一次重启来查看正在发生的事情.它说的是"输入要使用的值而不是(FDEFINITION'#:G3219)." 因此,对于已编译的代码,CLISP引用符号并按名称引用它.
虽然这种行为似乎符合标准.可以在HyperSpec中找到以下定义:
功能标志 ñ.功能的指示符; 也就是说,一个表示函数的对象,它是以下之一:符号(表示在全局环境中由该符号命名的函数),或函数(表示自身).如果将符号用作函数指示符但是它没有作为函数的全局定义,或者它具有作为宏或特殊形式的全局定义,则后果是未定义的.另请参见扩展函数指示符.
我认为一个未处理的符号匹配"一个符号被用作函数指示符,但它没有一个全局定义作为一个函数",用于未指明的后果.
编辑3 :(我可以同意,我不确定CLISP的行为是否是一个错误.对标准术语的细节更有经验的人应该判断这一点.它归结为是否是一个未加工符号的功能单元 - 即只能通过直接保留符号对象而不能通过名称引用的符号 - 将被视为"全局定义"或不符合
无论如何,这是一个示例解决方案,通过实现一次性包中的符号来解决CLISP中的问题,避免了未处理符号的问题:
(defmacro test () (let* ((pkg (make-package (gensym))) (name (intern (symbol-name (gensym)) pkg))) `(progn (defun ,name () (format t "Hello!")) (,name)))) (test)
编辑4:正如Joshua Taylor在对该问题的评论中指出的那样,这似乎是(10岁)CLISP错误#180的情况.
我测试过该bug报告建议两种解决方法,发现更换progn
与locally
实际上没有帮助,但取代它let ()
呢.