为什么可以';t CLISP调用某些具有未知名称的函数?

为什么可以';t CLISP调用某些具有未知名称的函数?,lisp,common-lisp,sbcl,clisp,Lisp,Common Lisp,Sbcl,Clisp,我已经编写了一个特殊的解析器生成器,它创建了将一个旧的、鲜为人知的7位字符集转换为unicode的代码。对解析器生成器的调用扩展为一组defuns,包含在progn中,然后进行编译。我只想将生成的defuns中的一个公开给系统的其余部分——顶层;所有其他函数都是解析器的内部函数,只能在顶级解析器的动态范围内调用。因此,生成的其他defun具有不相关的名称(使用gensym创建)。该策略在SBCL中运行良好,但我最近首次在CLISP中对其进行了测试,结果出现如下错误: *** - FUNCALL:

我已经编写了一个特殊的解析器生成器,它创建了将一个旧的、鲜为人知的7位字符集转换为unicode的代码。对解析器生成器的调用扩展为一组
defun
s,包含在
progn
中,然后进行编译。我只想将生成的
defun
s中的一个公开给系统的其余部分——顶层;所有其他函数都是解析器的内部函数,只能在顶级解析器的动态范围内调用。因此,生成的其他
defun
具有不相关的名称(使用
gensym
创建)。该策略在SBCL中运行良好,但我最近首次在CLISP中对其进行了测试,结果出现如下错误:

*** - FUNCALL: undefined function #:G16985
CLISP似乎无法处理名称不相关的函数。(有趣的是,系统编译时没有问题。)EDIT:在大多数情况下,它似乎可以处理名称不相关的函数。见下面Rörd的答案

我的问题是:这是CLISP的问题,还是某些实现(如SBCL)碰巧克服了公共Lisp的局限性

编辑:

例如,顶级生成函数(称为
parse
)的宏扩展有如下表达式:

(PRINC (#:G75735 #:G75731 #:G75733 #:G75734) #:G75732)
(defun #1=#:fact (n &optional (acc 1))
  (if (zerop n) acc
      (#1# (1- n) (* acc n))))
计算此表达式(通过调用
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

编辑:


因此,用户Joshua Taylor指出,这是由于。

我希望我正确处理了这个问题。对我来说,它在CLISP中工作

我试着这样做:使用宏创建一个具有GENSYM ed名称的函数

(defmacro test ()  
  (let ((name (gensym)))  
    `(progn  
       (defun ,name (x) (* x x))  
       ',name)))

现在我可以得到名称
(setf x(test))
并将其命名为
(funcall x 2)

您没有显示给出错误的行,因此我只能猜测,但据我所知,唯一可能导致这个问题的是,当你试图调用它时,你指的是符号的名称,而不是符号本身

如果您引用的是符号本身,那么lisp实现所要做的就是查找该符号的
符号函数
。不管是否被拘留都不重要

请问您为什么没有考虑另一种隐藏函数的方法,即
标签
语句或在只导出一个外部函数的新包中定义函数

编辑:以下示例是从与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的行为取决于是直接解释代码还是先编译后解释代码。您可以通过在CLISP中直接
load
ing一个Lisp文件,或者首先在其上调用
compile file
,然后
load
ing FASL来查看差异

您可以通过查看CLISP提供的第一次重启来了解发生了什么。它的意思类似于“输入一个要使用的值,而不是(FDEFINITION'#:G3219)”,因此对于编译代码,CLISP引用符号并按名称引用它

这种行为似乎符合标准。可以在HyperSpec中找到以下定义:

功能指示符。函数的指示符;也就是说,一个表示函数的对象,它是以下对象之一:符号(表示在全局环境中由该符号命名的函数)或函数(表示自身)。如果一个符号用作函数指示符,但它没有作为函数的全局定义,或者它有作为宏或特殊形式的全局定义,则结果是未定义的。另请参见扩展功能指示符

我认为一个非预期符号与“一个符号被用作函数指示符,但它没有作为函数的全局定义”的情况相匹配,用于未指定的结果

编辑3:(我可以同意,我不确定CLISP的行为是否是一个bug。有经验的标准术语的细节的人应该判断这一点,归结到一个未被嵌入的符号的功能单元,即一个不能被名字引用的符号,只有直接持有符号对象,才会被考虑。(是否定义了“全球定义”)

无论如何,下面是一个示例解决方案,它通过将符号放入一次性软件包中来解决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岁)的一个案例


我已经测试了该bug报告中建议的两种解决方法,发现用
本地
替换
progn
实际上没有帮助,但用
let()
替换它确实有帮助。

您可以很确定地定义名称为不相关符号的函数。例如:

CL-USER> (defun #:foo (x)
           (list x))
#:FOO
CL-USER> (defparameter *name-of-function* *)
*NAME-OF-FUNCTION*
CL-USER> *name-of-function*
#:FOO
CL-USER> (funcall *name-of-function* 3)
(3)
但是,每次读取此类表单时,语法都会引入一个新符号:

#:引入名称为symbol name的非预期符号。每次遇到此语法时,都会创建一个不同的非预期符号。符号名称必须为
CL-USER> (eq '#:foo '#:foo)
NIL
CL-USER> (#:foo 3)
; undefined function #:foo error
(defun #1=#:fact (n &optional (acc 1))
  (if (zerop n) acc
      (#1# (1- n) (* acc n))))
CL-USER> (pprint '(defun #1=#:fact (n &optional (acc 1))
                    (if (zerop n) acc
                        (#1# (1- n) (* acc n)))))

(DEFUN #:FACT (N &OPTIONAL (ACC 1))
  (IF (ZEROP N)
      ACC
      (#:FACT (1- N) (* ACC N))))
CL-USER> (DEFUN #:FACT (N &OPTIONAL (ACC 1))
           (IF (ZEROP N)
               ACC
               (#:FACT (1- N) (* ACC N))))

; in: DEFUN #:FACT
;     (#:FACT (1- N) (* ACC N))
; 
; caught STYLE-WARNING:
;   undefined function: #:FACT
; 
; compilation unit finished
;   Undefined function:
;     #:FACT
;   caught 1 STYLE-WARNING condition
(defmacro foo (arg)
   (let ((g (gensym))
         (literal '(blah ,g ,g ,arg)))
      ...))

(defun bar ()
  (foo 42))