LISP - 包
Lisp - 用于代码组织的包(Packages)
Section titled “Lisp - 用于代码组织的包(Packages)”随着程序的增长,您不可避免地会遇到函数、变量和类共享相同名称的情况。为了防止这些名称冲突,Lisp 提供了一种强大的命名空间机制,称为 包(packages)。包是一个符号表,它将名称(字符串)映射到符号。
可以把它想象成您代码名称的目录。my-function 在 package-a 中的符号与 my-function 在 package-b 中的符号是完全不同的对象。
当前包存储在全局变量 *package* 中。默认情况下,您通常从 COMMON-LISP-USER 包开始。
defpackage:定义包的标准方式
Section titled “defpackage:定义包的标准方式”创建包的现代、声明式方法是使用 defpackage 宏。最佳实践是将所有 defpackage 形式放在一个单独的文件中,通常命名为 package.lisp,并在加载任何其他代码之前加载它。
defpackage 宏有几个重要的选项:
- :use:指定要从哪些其他包继承符号。几乎总是包含
:common-lisp。 - :export:定义包的 公共 API 的符号列表。这些符号可以在其他包中直接访问,无需包前缀。
- :import-from:将特定符号从另一个包导入到当前包的命名空间中。
- :nicknames:包的更短、替代名称列表。
现代项目结构示例
Section titled “现代项目结构示例”让我们创建一个简单的计算器小项目,看看包在实践中如何工作。我们将包含两个文件:package.lisp 和 main.lisp。
文件:package.lisp
Section titled “文件:package.lisp”;; package.lisp 文件
(defpackage #:my-calculator (:use #:common-lisp) (:export #:add #:subtract))在这里,我们定义了一个名为 my-calculator 的包。它使用了 common-lisp 中的所有标准符号,并导出了两个符号:add 和 subtract。这些是我们希望程序的其他部分(或其他人)使用的公共函数。
文件:main.lisp
Section titled “文件:main.lisp”;; main.lisp 文件
(in-package #:my-calculator)
;; 这是一个内部的、未导出的辅助函数。;; 其他包无法轻易访问它。(defun log-operation (op x y) (format t "Performing ~a on ~a and ~a~%" op x y))
;; 这是一个导出的函数,属于我们的公共 API 的一部分。(defun add (x y) (log-operation 'add x y) (+ x y))
;; 这是另一个导出的函数。(defun subtract (x y) (log-operation 'subtract x y) (- x y))顶部的 (in-package ...) 声明会切换当前包。之后的所有 defun 形式都将在 my-calculator 包内创建符号。
现在,让我们看看如何使用我们新的 my-calculator 包。想象这可能是您应用程序的不同部分,或者您正在 REPL(读取-求值-打印循环)中使用它。
;; 假设上述文件已加载。
;; 要使用我们的包,我们可以通过包名限定符号名。(print (my-calculator:add 10 5))
;; 我们也可以在另一个包定义中使用 :use 选项,;; 或者在交互式环境中使用 `use-package` 函数。(use-package '#:my-calculator)
;; 现在导出的符号可以直接使用。(print (subtract 10 5))
;; 尝试访问内部符号将导致错误或警告。;; 双冒号 `::` 用于访问内部符号,但这;; 强烈不推荐,因为它破坏了封装性。;; (my-calculator::log-operation 'test 1 2) ; <-- 不要这样做!Performing ADD on 10 and 515Performing SUBTRACT on 10 and 55现代工具:ASDF 和 Quicklisp
Section titled “现代工具:ASDF 和 Quicklisp”在实际的 Common Lisp 项目中,您不会手动加载文件。您会使用 ASDF(Another System Definition Facility,另一个系统定义工具) 将您的项目定义为“系统”。一个 .asd 文件告诉 Lisp 哪些文件属于您的系统及其依赖关系。
此外,Common Lisp 事实上的包管理器是 Quicklisp。它允许您通过一个命令(例如 (ql:quickload :library-name))下载和管理数百个开源库。掌握包、ASDF 和 Quicklisp 是现代专业 Common Lisp 开发的基础。