当您在调用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,这是你应该用于真实的(非 原型)代码。“
上面的代码中是否有代码味道?另外,你如何导出其他符号(猫狗动物类型等等 - 有很多它们)以避免重复?
答案 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中更容易的探索。