Common Lisp:以编程方式创建的defpackage和导出符号

时间:2013-09-20 23:34:25

标签: common-lisp packages

当您在调用defpackage宏时尚未创建符号时,如何从包中导出符号?

(defpackage :package-a
  (:use :cl)
  (:export :fruit-type :animal-type :orange :apple :peach :cat :dog))

(deftype fruit-type () '(member ORANGE APPLE PEACH))
(deftype animal-type () '(member CAT DOG))

(defparameter *other-symbol-names*
  '("A1" "A2" "B1" "B2")) ;imagine a longer list here
                          ;with names generated by a function

(defparameter *other-symbols*
  (mapcar #'(lambda (sym-name)
          (import (make-symbol sym-name))
          (find-symbol sym-name))
      *other-symbol-names*))

(mapcar #'export *other-symbols*)

(setf A1 32 A2 33 B1 34 B2 35)

还有另一个包

(defpackage :package-b
  (:use :cl :package-a))
(in-package :package-b)
(format nil "~a ~a ~a ~a" |A1| |A2| |B1| |B2|)

我读过“The Complete Idiot's Common Lisp Packages指南” “现在你已经了解了所有可能的无数功能和宏 用来操纵你不应该真正使用它们的包。代替, IMPORT,EXPORT,SHADOW等的所有功能都汇集在一起 单个宏称为DEFPACKAGE,这是你应该用于真实的(非 原型)代码。“

上面的代码中是否有代码味道?另外,你如何导出其他符号(猫狗动物类型等等 - 有很多它们)以避免重复?

1 个答案:

答案 0 :(得分:1)

如果不了解更多有关您的意图和要求的话,很难说很多,但在许多情况下,最好有一个或多个包含动态生成的对象的哈希表(或类似),然后导出符号对于您的哈希表。

这是一个关于如何运作的手工波浪的例子。如果您可以编辑并添加有关您的要求和约束的更多信息,我会看到我是否可以提供更多帮助。

(in-package :cl)

(defpackage :package-a
  (:use :cl)
  (:export *objects* put get)
  (:shadow get))

(in-package :package-a)

(defvar *objects* (make-hash-table)
  "Container for dynamically generated objects we want to expose to the
  package's user.")

(defun put (name obj)
  (setf (gethash name *objects*) obj))

(defun get (name &optional default)
  (gethash name *objects* default))

;; Your code can put arbitrary objects into the hash table
(put :foo (lambda () :a-thunk))
(put :bar (lambda () :another))

;; And your users can retrieve them
(in-package :cl-user)
(use-package :package-a)
(funcall (get :foo)) ;; => :a-thunk

我使用了名称而不是符号的关键字,因为关键字不是包的本地关键字(或者更具体地说,它们都是keyword包的本地关键字。如果您改为使用{{1} }和'foo您将需要导出这些符号,或者您的用户在引用它们时需要使用包指示符(例如'bar)。

您也可以使用字符串作为键,但在这种情况下,您可能希望使用(get 'package-a::foo)创建表。默认的哈希表测试是(make-hash-table :test 'equal),它不会恰当地比较字符串。将关键字与#'eql进行比较比将字符串与#'eql进行比较要快(因为关键字是一个简单的指针比较,而不是字符串所需的逐字符比较),但差异可能微不足道,除非你有特别的理由不这么认为。

这种方法为您的用户提供了更好的界面,因为现在您已经定义了入口点,文档字符串的机会,默认值以及在REPL中更容易的探索。