以编程方式加载 Common Lisp 模块

Load Common Lisp Module Programmatically

我几周前才开始学习 Common Lisp,如果这是一个显而易见的问题,我深表歉意。如何以编程方式加载模块?我有一个“任务”目录,每个都是一个 Lisp 程序,我想导入每个和 运行 它们都包含的特定函数。

我找到了一种遍历目录的方法(通过 UIOP:DIRECTORY-FILES)。但是我一直在试图找出一种方法来将模块“作为”特定名称加载,如 Python 中所示。这将允许我 load "module-1.lisp" as mod 然后 load "module-2.lisp" as mod 循环。

伪代码:

for path in directory
    (load path as mod)
    (mod:function)

如果有更好的方法来实现我想要做的事情,请尽管说出来!在此先感谢您的帮助!

软件包和系统的快速摘要

"加载为" 在 Common Lisp 中没有意义,因为加载与定义新系统或包不同。在 Python 中,文件是模块等,但在 Lisp 中不是。

Lisp 有一个 packages 的概念,它们是名称空间。它们用于组织符号(符号是第一个-class值)。换句话说,他们使用、导出或可能隐藏来自其他包的符号,仅此而已。当你计算 (in-package pack) 时,你可以直接从 pack 中写出所有 可访问的 符号,如 my-fun,否则你需要完全限定符号,如 some-package:their-func。 有些扩展允许您导入具有不同名称的包,但这不是标准的(参见 https://github.com/phoe/trivial-package-local-nicknames

加载 Lisp 脚本不一定与定义包相同,它取决于文件中的内容(如果它是 defpackage 形式)。

当您想用 Lisp 组织源代码时,您可以使用 ASDF 定义一个 系统。一个系统列出了它的所有依赖关系,它的组件(文件)以及它们的组件之间的依赖关系。这就是您如何描述文件应该以何种顺序加载、编译、测试等。

包和系统是独立的,但在小型系统中,系统通常具有相同的名称和它定义的唯一包。对于较大的系统,可能有多个包。

参见 21. Programming in the Large: Packages and Symbols

你的问题

您的每个文件都需要定义一个包,以避免污染单个命名空间,例如:

(defpackage :common-name.01
   (:use :cl :utils)
   (:export #:run-me))

(in-package :common-name.01)

;; some code

但是一旦它们全部加载完毕,无论是使用您的方法还是通过定义适当的 ASDF 系统,您都希望能够访问所有包中的所有 RUN-ME 功能。

您可以编写一些列出包等的内省代码,但我认为更好的方法是让每个文件都有一种方法声明它们的RUN-ME 函数应该像测试框架一样在您的框架中注册。

例如,在文件末尾,您可以写:

(provide-function 'run-me)

这假设例如您的 utils 包定义并导出一个名为 provide-function 的函数,该函数将值存储在中央注册表中。

例如,在utils.lisp中:

(defpackage :utils
   (:use :cl) 
   (:export #:provide-function
            #:run-all))

(in-package :utils)

(defvar *all-interesting-functions* nil)

(defun provide-function (f)
  (pushnew f *all-interesting-functions* :test #'eql))

当你想要运行所有函数时,你可以遍历这个列表:

(defun run-all ()
  (mapcar #'funcall *all-interesting-functions*))