包(Package)

Table of Contents

1 Symbol的有效范围

Common Lisp用package来限定symbol的有效范围,全局变量*package*记录了当前所在包(叫作:home-package):

CL-USER> *package*
#<PACKAGE "COMMON-LISP-USER">

Common Lisp启动后默认在common-lisp-user包里。

当REPL捕获到一个symbol时,首先在当前包中查找symbol是否存在,否则就创建一个。通过find-symbol可以查找symbol。

相关术语:

  • external:在定义package时,指定export的符号
  • internal:未导出的符号
  • inherited:defpackage时,被:use的包称为继承包(使用:use时,只有外部包可以被导入)
  • accessible:1、可访问,只能通过find-symbol找到的符号;2、非限定名指向的符号(就是不通过“:”引用的符号)
  • present:在包里能找到符号,就是present

2 自带的三个标准包

  • common-lisp-user,别名是cl-user。
  • common-lisp,别名是cl。
  • keyword,任何包都可以访问keyword包里的符号。

3 defpackage

defpackage用于定义一个新的包,例:

(defpackage :cl-tld
  (:use :cl)
  (:export :get-tld :get-domain-suffix :test-cl-tld))

参数说明:

  • :import-from,指定符号是从哪个包中导出:
(defpackage #:test1 (:import-from :common-lisp :+)) ; #:test1这个包里,除了“+”,都必须加包名引用
  • :shadow:不继承指定的符号
  • :shadowing-import-from:指定符号从哪个包继承

4 use-package

把accessible的符号导到当前包里。

5 make-package

一般来说,建议用defpackage。make-package和defpackage区别如下:

  • make-package可选参数没defpackage多,比如没有导出符号的参数,得这样写:
(eval-when (load compile eval)
  (unless (find-package :just-test)
    (make-package :just-test
		  :use '(cl))))

(make-package :just-test :use '(cl))

(in-package :just-test)

(export 'heihei) ;; 得手动export符号

(defun heihei ()
  "heihei")
  • make-package返回包名,可以绑定到符号上:
(defvar just-test (make-package :just-test))
  • 需要手动eval-when,如果写成:
(make-package just-test :use '(cl))

在编译时会报错:

(compile-file "x.lisp") ; => Package JUST-TEST does not exist.

6 销毁包

使用unuse-package可销毁use-package导入的所有符号。

CL-USER> (use-package :hi)
T
CL-USER> (say-hi) ; say-hi是hi包里export的函数
"hi"
CL-USER> (unuse-package :hi)
T
CL-USER> (say-hi) ; => undefined function: SAY-HI

delete-package可以销毁加载的包。

7 与package有关的函数

(list-all-packages),列出当前加载的所有package。 (package-used-by-list),列出指定的包被哪些包use了。