Common Lisp:从包中临时导入一些函数的最佳方法

时间:2012-01-06 21:04:21

标签: macros lisp common-lisp

有没有办法使用标准的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编写带有导入的宏。

2 个答案:

答案 0 :(得分:2)

它使运行时函数调用成本更高:它包含一个arg列表,在一个包中查找一个符号,通过符号的函数单元调用一个函数。

它只能通过符号,而不是词法功能。这使得它在通过宏生成代码的情况下不太有用。

它的命名令人困惑。 'import'是一个包操作,包只处理符号,而不是函数。您无法在包中导入函数,只能导入符号。

(labels ((foo () 'bar))
  (foo))

词法函数名FOO仅在源代码中包含符号。以后无法通过其源符号访问该函数(例如,使用(symbol-function 'foo))。如果编译器将编译上面的代码,则不需要保留符号 - 除了用于调试目的之外,不需要它。您对APPLY的调用将无法找到LABELS或FLET创建的任何函数。

您的宏不导入符号,它会创建一个本地词法函数绑定。

对于略微相似的宏,请参阅CL:WITH-SLOTSCL: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:symbolpackage::symbol)一起使用,然后unintern