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

Macros CommonLisp:从包中临时导入一些函数的最佳方法,macros,lisp,common-lisp,Macros,Lisp,Common Lisp,是否有办法使用标准的通用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 lexica

是否有办法使用标准的通用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> 
(defclass默认测试类()
((一份5“文件”)
(b 4“文件”))
#
CL-USER>
(带函数(类直接插槽插槽定义名称)'sb mop
(带函数(插槽定义初始化窗体)'sb mop
(插槽定义初始化窗体)
(car(类直接插槽(查找类“测试类()()))))
5.
CL-USER>
编辑:将雷纳的一些建议纳入宏

我决定保留运行时查找功能,以运行时查找在包中查找函数的时间为代价

我试图编写一个with import宏,该宏使用阴影导入和UNTREN,但无法使其工作。我对读者提出的问题是,在对导入函数的代码进行求值之前,导入的函数还不存在(在读取时)

我认为让它与shadowing import和untrn一起工作是一个更好的方法,因为这将更干净、更快(尽管没有运行时查找功能),并且可以与包中的函数和符号一起工作


我很想知道是否有人可以使用untrn和shadowing import使用import宏编写代码。

您可以使用
import
和合格符号列表(即
package:symbol
package::symbol
)您需要导入它们,然后
untrn
它们。

这使得运行时函数调用的成本更高:它使用参数列表,在包中查找符号,通过符号的函数单元调用函数

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

它的名字令人困惑“导入”是一个包操作,包只处理符号,不处理函数。不能在包中导入函数,只能导入符号

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

宏不导入符号,而是创建本地词法函数绑定

有关稍微相似的宏,请参见
CL:WITH-SLOTS
CL:WITH-ACCESSORS
。它们不支持运行时查找,但允许高效编译

您的宏不是这样嵌套的(这里使用“CLOS”作为包,就像您的“SB-MOP”):

生成的代码是:

(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))))

这不适用于词法函数,因为APPLY不能使用符号作为名称来调用它们。我认为这在宏中是不可能的,因为符号是在宏展开之前由读取器解析的,但我可能错了,我还不完全理解读取器是如何工作的。@Daimrod应该可以构造这样一个宏,但这是非常棘手的(可能是不可移植的),因为您必须处理由早期符号解析策略引起的名称冲突。@VsevolodDyomkin:Ok,但我认为这可以通过reader宏和code walker来完成,但对于我来说,这似乎是一个小小的改进,需要做很多工作。对于您试图在源文件中多次切换包的操作,这可能是一个更好的解决方案吗?我在理解您的用例方面遇到了困难。“函数”arglist中不应该有任何“:”。函数arglist具有表示函数名称的符号,并且包是这些函数所在的位置。这与插槽和存取器类似。我认为您编写的代码应该是这样的:(使用import(class directslots)'clos(class directslots(find class'testclass))@claytontstanley:为什么不呢?p1::class direct slots是一个合法的函数名。WITH-SLOTS或WITH-ACCESS在处理此类名称时没有问题。我的示例显示,在宏中不可能使用嵌套的WITH-IMPORT生成一个函数,然后另一个函数尝试使用它。请记住:如果您处理常见的Lisp宏,那么您不仅要编写最终用户以简单方式使用的构造(您必须记录和/或检查其限制),而且所有内容都可能是由其他代码(->宏)生成的更复杂代码的一部分。
(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))))