Common lisp 为什么使用defpackage会导致名称冲突?

Common lisp 为什么使用defpackage会导致名称冲突?,common-lisp,sbcl,slime,Common Lisp,Sbcl,Slime,因此,我正在使用ProjectEuler练习lisp,我正在将一些实用程序函数收集到一个单独的文件中,以减少重复,我预计它可能会变得非常庞大,所以我已经开始做一个包定义。下面是该文件的压缩版本,它仍然说明了我的问题: (defpackage :euler-util (:use :common-lisp) (:export :divisible-by)) (in-package :euler-util) (defun divisible-by (x y) (equal

因此,我正在使用ProjectEuler练习lisp,我正在将一些实用程序函数收集到一个单独的文件中,以减少重复,我预计它可能会变得非常庞大,所以我已经开始做一个包定义。下面是该文件的压缩版本,它仍然说明了我的问题:

(defpackage :euler-util
    (:use :common-lisp)
    (:export :divisible-by))

(in-package :euler-util)

(defun divisible-by (x y)
    (equal 0 (mod x y)))
我可以编译并加载(C-C-k)该文件到SLIME中,而不会出现任何警告或错误。现在,当我在另一个文件中使用它时,我会执行以下操作:

(load "util.lisp")
(use-package :euler-util)

(defun euler-1 ()
    (loop for i from 3 to 999 when (or (divisible-by i 3) (divisible-by i 5)) sum i))
当我试图编译并加载该文件时,会出现如下错误:

;;;; my-project.asd

(asdf:defsystem my-project
  :serial t
  :components ((:file "util")
               (:file "my-project")))
“USE-PACKAGE#导致以下符号之间的#名称冲突:EULER-UTILS:DIVISIBLE-BY,COMMON-LISP-USER::DIVISIBLE-BY[类型名称冲突的条件]”

为什么这个符号会出现在COMMON-LISP-USER包中,我该如何阻止它?

注意:我写这个答案是为了回应我对这个问题的最初理解,我认为这个图像仍然是以前代码中的“脏”图像。这是一种常见的此类症状。然而,这种情况是由于
use-package
的效果不是在编译时发生的,而是在加载时发生的。因此,当我
加载下面的文件时,没有问题,但是在按OP原样编译它时,有一个对
cl user::divisible by
的引用。我把这个答案留在这里是因为有这个问题的人仍然有可能找到它,但它与OP的问题并不完全相同

您的代码: 我已经在
util.lisp
file2.lisp
中制作了您的代码的本地副本:

$cat util.lisp
(defpackage:euler-util)
(:用法:common lisp)
(:导出:可除以)
(包装中:euler-util)
(defun可被(x y)整除)
(等于0(模x y)))
$cat file2.lisp
(加载“util.lisp”)
(使用包:euler-util)
(defun euler-1()
(当(或(可被i3整除)(可被i5整除))和i时,从3到999的i循环)
重现问题 这个问题很容易重现:如果您以某种方式将名为
“DIVISIBLE-BY”
的符号插入到
CL-USER
包中,然后尝试使用导出同名符号的包,您将遇到以下问题:

$sbcl--noinfo格式
*(defun可被(&rest参数)整除)(declare(ignore参数)))
可除以
*(加载“文件2”)
在线程#中的名称冲突上调用调试器:
USE-PACKAGE#导致中的名称冲突
#在以下符号之间:
EULER-UTIL:DIVISIBLE-BY,COMMON-LISP-USER::DIVISIBLE-BY
另见:
ANSI标准,第11.1.1.2.5节
键入“帮助”以获取调试器帮助,或键入(SB-EXT:QUIT)以退出SBCL。
重新启动(可通过数字或可能的缩写名称调用):
0:[解决冲突]解决冲突。
1:[重试]重试评估当前顶级表单。
2:[继续]忽略错误并继续加载文件“../file2.lisp”。
3:[中止]中止加载文件“../file2.lisp”。
4:退出调试器,返回顶层。
(名称冲突)
#
使用软件包
#
EULER-UTIL:可被整除
可除的
解决冲突 如果发生这种情况,可以手动解决。我在这里的命令行上使用SBCL,但是(我希望)您应该在SLIME中获得类似的调试器选项。为了简洁起见,我在命令行中加入了很多内容。这里的要点是:
--noinfo
防止横幅被打印
--eval“'divisible-by”
确保有一个符号
cl user:divisible-by
;和
--加载文件2.lisp
加载您的文件。也就是说,我们在命令行上重新创建了问题,以便我们可以集中精力解决问题

$sbcl--noinfo--eval“'divisible-by”--加载文件2.lisp
在线程#中的名称冲突上调用调试器:
USE-PACKAGE#导致中的名称冲突
#在以下符号之间:
EULER-UTIL:DIVISIBLE-BY,COMMON-LISP-USER::DIVISIBLE-BY
另见:
ANSI标准,第11.1.1.2.5节
键入“帮助”以获取调试器帮助,或键入(SB-EXT:QUIT)以退出SBCL。
重新启动(可通过数字或可能的缩写名称调用):
0:[解决冲突]解决冲突。
1:[重试]重试评估当前顶级表单。
2:[继续]忽略错误并继续加载文件“/home/taylorj/tmp/package issue/file2.lisp”。
3:[中止]中止加载文件“/home/taylorj/tmp/package issue/file2.lisp”。
4:忽略运行时选项--加载“file2.lisp”。
5:跳过其余的--eval和--load选项。
6:跳至顶层读取/评估/打印循环。
7:[退出]退出SBCL(调用#'退出,终止进程)。
(名称冲突)
#
使用软件包
#
EULER-UTIL:可被整除
可除的
现在,如果要解决冲突,可以键入
0

0]0
选择要在程序包COMMON-LISP-USER中访问的符号:
1.EULER-UTIL:可被整除
2.COMMON-LISP-USER::可除
输入一个整数(介于1和2之间):1
通过将
cl-user
中可访问的符号
euler-util:整除,您的代码将按预期工作:

*(euler-1)
233168
有了新的形象,就没有问题了。 虽然你可能没有像我上面所说的那样造成问题,但可能是类似的。您似乎是在一个非全新的环境中加载代码(例如,您已经输入了一些代码,导致读者在
CL-USER
包中插入
“DIVISIBLE-BY”
)。使用全新的Lisp,您的代码可以正常加载:

$sbcl--load file2.lisp--noinfo格式
*(欧拉-1)
233168

编译文件
读取文件中的所有表单,并仅执行展开为适当表达式的表单。在上面的示例中,直到已读取引用文件(将其所有符号插入cl user中)后才会加载util.lisp文件。是否类似
(defpackage #:euler
  (:use #:cl #:euler-util))

(in-package #:euler)
;;;; my-project.asd

(asdf:defsystem my-project
  :serial t
  :components ((:file "util")
               (:file "my-project")))