2

当您在调用 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 Guide to Common Lisp Packages”中读到“现在您已经了解了可用于操作包的无数函数和宏,您不应该真正使用它们中的任何一个。相反,所有IMPORT、EXPORT、SHADOW 等的功能都汇总在一个名为 DEFPACKAGE 的宏中,这是您应该用于真实(非原型)代码的宏。”

我上面的代码中是否有代码异味?此外,您将如何导出其他符号(猫狗动物类型等——其中有很多)以避免重复?

4

1 回答 1

3

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

这是如何工作的手动示例。如果您可以编辑并添加有关您的要求和约束的更多信息,我会看看是否可以提供更多帮助。

(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包本地的。如果您改为使用'foo并且'bar您将回到需要导出这些符号,或者您的用户在引用它们时需要使用包代号(例如(get 'package-a::foo))。

您也可以使用字符串作为键,但在这种情况下,您可能希望使用(make-hash-table :test 'equal). 默认哈希表测试是#'eql,它不会适当地比较字符串。比较关键字 with#'eql比比较字符串 with 更快#'equal(因为关键字是简单的指针比较,而不是字符串所需的逐个字符比较),但差异可能微不足道,除非您有特定的理由不这么认为。

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

于 2013-09-21T01:47:58.663 回答