Lee*_*ree 5 sbcl common-lisp slime
因此我正在使用Project Euler练习lisp,我正在将一些小实用程序函数收集到一个单独的文件中以减少重复,我希望它可能会变得非常庞大,所以我已经完成了包定义.这是该文件的精简版,仍然说明了我的问题:
(defpackage :euler-util
(:use :common-lisp)
(:export :divisible-by))
(in-package :euler-util)
(defun divisible-by (x y)
(equal 0 (mod x y)))
Run Code Online (Sandbox Code Playgroud)
我可以编译并加载(Cc Ck)该文件到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))
Run Code Online (Sandbox Code Playgroud)
当我尝试编译并加载THAT文件时,我收到如下错误:
"USE-PACKAGE#导致以下符号之间的#中的名称冲突:EULER-UTILS:DIVISIBLE-BY,COMMON-LISP-USER :: DIVISIBLE-BY [NAME-CONFLICT类型的条件]"
为什么这个符号出现在COMMON-LISP-USER包中,如何阻止它?
compile-file读取文件中的所有表单,并仅执行扩展为适当的eval-when表达式的表单.在上面的示例中,只有在读取了引用文件(在cl-user中实现其所有符号)之后才会加载util.lisp文件.use-package同样是一个普通的函数调用,直到加载时才被评估,当你要求它用当前包中可访问的相同名称创建两个不同的符号时.
一种选择是将load和use-package语句放在一个eval-when表单中,例如
(eval-when (:compile-toplevel :load-toplevel :execute)
(load "util.lisp")
(use-package :euler))
Run Code Online (Sandbox Code Playgroud)
我认为为项目定义一个新包更好,并将其放在文件的顶部:
(defpackage #:euler
(:use #:cl #:euler-util))
(in-package #:euler)
Run Code Online (Sandbox Code Playgroud)
这些语句在编译时自动评估,因此不需要eval-when.
经验丰富的CL作者倾向于通过以特定顺序定义包,将in-package表单放在每个源文件中,以及使用系统定义工具以正确的顺序编译和加载文件来避免此问题.
我使用ASDF作为系统定义工具.一个简单的系统文件可能如下所示:
;;;; my-project.asd
(asdf:defsystem my-project
:serial t
:components ((:file "util")
(:file "my-project")))
Run Code Online (Sandbox Code Playgroud)
如果您将该文件放在ASDF知道的某个位置,(asdf:load-system "my-project")那么将按指定的顺序编译和加载文件.
如果你使用Quicklisp,一个简单的方法就是将项目目录放在〜/ quicklisp/local-projects /中,然后用(ql:quickload "my-project")它来自动加载它及其依赖项.