有没有办法使用标准的common-lisp函数/宏将包中的一些函数临时导入到当前包中?
我找不到一个,不得不自己滚动。如果标准已经提供了这样的功能,我宁愿不必编写任何代码,或引入另一种语言结构。
(defmacro with-functions (functions the-package &body body)
"Allows functions in the-package to be visible only for body.
Does this by creating local lexical function bindings that redirect calls
to functions defined in the-package"
`(labels
,(mapcar (lambda (x) `(,x (&rest args)
(apply (find-symbol ,(format nil "~:@(~a~)" x)
,the-package)
args)))
functions)
,@body))
使用示例:
(defclass-default test-class ()
((a 5 "doc" )
(b 4 "doc")))
#<STANDARD-CLASS TEST-CLASS>
CL-USER>
(with-functions (class-direct-slots slot-definition-name) 'sb-mop
(with-functions (slot-definition-initform) 'sb-mop
(slot-definition-initform
(car (class-direct-slots (find-class 'test-class))))))
5
CL-USER>
编辑:将一些Rainer的建议纳入宏。
我决定保留运行时查找功能,以运行时查找的时间成本来查找包中的函数。
我尝试编写一个使用shadowing-import和unintern的with-import宏,但我无法让它工作。我有读者的问题,在导入函数的代码被评估之前,导入的函数尚未存在(在读取时)。
我认为让它与shadowing-import和unintern一起工作是一个更好的方法,因为这会更清晰,更快(虽然没有运行时查找功能)并且可以在包中使用函数和符号。
我很想知道是否有人可以使用unintern和shadowing-import编写带有导入的宏。
答案 0 :(得分:2)
它使运行时函数调用成本更高:它包含一个arg列表,在一个包中查找一个符号,通过符号的函数单元调用一个函数。
它只能通过符号,而不是词法功能。这使得它在通过宏生成代码的情况下不太有用。
它的命名令人困惑。 'import'是一个包操作,包只处理符号,而不是函数。您无法在包中导入函数,只能导入符号。
(labels ((foo () 'bar))
(foo))
词法函数名FOO
仅在源代码中包含符号。以后无法通过其源符号访问该函数(例如,使用(symbol-function 'foo)
)。如果编译器将编译上面的代码,则不需要保留符号 - 除了用于调试目的之外,不需要它。您对APPLY的调用将无法找到LABELS或FLET创建的任何函数。
您的宏不导入符号,它会创建一个本地词法函数绑定。
对于略微相似的宏,请参阅CL:WITH-SLOTS
和CL:WITH-ACCESSORS
。那些不支持运行时查找,但允许有效的编译。
你的宏不会像这样嵌套(这里使用“CLOS”作为包,就像你的“SB-MOP”):
(defpackage "P1" (:use "CL"))
(defpackage "P2" (:use "CL"))
(with-import (p1::class-direct-slots) 'CLOS
(with-import (p2::class-direct-slots) 'P1
(p2::class-direct-slots (find-class 'test-class))))
生成的代码是:
(LABELS ((P1::CLASS-DIRECT-SLOTS (&REST ARGS)
(APPLY (FIND-SYMBOL "CLASS-DIRECT-SLOTS" 'CLOS) ARGS)))
(LABELS ((P2::CLASS-DIRECT-SLOTS (&REST ARGS)
(APPLY (FIND-SYMBOL "CLASS-DIRECT-SLOTS" 'P1) ARGS)))
(P2::CLASS-DIRECT-SLOTS (FIND-CLASS 'TEST-CLASS))))
答案 1 :(得分:1)
您可以将import
与要导入的合格符号列表(例如package:symbol
或package::symbol
)一起使用,然后unintern
。