将软件包名称添加到符号

时间:2018-08-31 16:52:14

标签: common-lisp symbols

这是在特定情况下的常规符号问题。我想我需要回答的问题是:给定一个包含符号'foo的参数,我如何处理符号的包部分,使其等同于'package:foo?

更具体的上下文:有一个单元测试程序包Fiveam,它将测试函数存储在哈希表(it.bese.fiveam::*tests)中,该函数具有像package::test-name这样的键,这些键哈希到包含测试的对象。要运行测试,通常将一个作为哈希键的符号传递给run!函数:(run! 'package::test-name。如果我从定义测试的run!包中调用tests,则可以在(run! 'test-name)上按C-x C-e键。从SLIME REPL打电话时,我必须做(tests::run! 'tests::test-name),给您带来不便。

我想使用(run! 'test-name)从cl-user运行测试,并省略软件包名称。我以为包裹run!并将丢失的包添加到符号中会很简单,但是我的所有尝试都失败了,部分原因是CL-USER:test-nametests:test-name是不同的。 / p>

有问题的哈希表使用eql测试相等性。在tests包中:

(eql 'tests::foo 'foo) => T

在CL-USER中:

(eql 'tests::foo 'foo) => nil

各种函数都可以返回符号(例如intern,make-symbol或format-symbol),但是它们都不等于哈希表中的键:

(eql (alexandria:format-symbol t "foo::bar") 'foo::bar) => nil
(eql (make-symbol "foo::bar") 'foo::bar) => nil

从SLIME / REPL / FIVEAM上下文中总结出我认为问题出在哪里,我不知道如何将'bar转换为'foo:bar作为符号。我可以手动将其指定为'foo:bar,但是在给定'foo:bar的情况下,我似乎无法将任何东西都分配给eql 'bar

注意:我不太在乎为程序包名称输入多余的几个字母。我在这里真正想做的是提高我对范围和指定符号的理解。

谢谢。

2 个答案:

答案 0 :(得分:1)

首先,我假设您可以使用扩展命令test切换到SLIME REPL中的in-package包(即,在REPL提示符下,键入逗号{ {1}}后跟,,系统将提示您输入软件包名称,这样切换软件包对您来说并不方便。

一种解决方案可能是在当前使用的任何程序包中引入一个小助手功能:

in-package

或更简单(并假设(defun run-test (name &optional (package 'test)) (funcall (find-symbol #.(symbol-name 'run!) package) (find-symbol (symbol-name name) package))) 的定义来自于Fiveam软件包,是通过run!:use进入:import软件包的)

test

您现在可以将其称为

(defun run-test (name &optional (package 'test))
   (it.bese.fiveam:run! (find-symbol (symbol-name name) package)))

请注意,我们在这里使用(run-test 'check-everything) 代替find-symbol,因为我们希望符号在所有情况下都存在。如果您希望该助手在任何地方都可以轻松访问,甚至可以

intern

现在您可以将其命名为

(defun :run (name &optional (package 'test))
   (it.bese.fiveam:run! (find-symbol (symbol-name name) package)))

in,与当前软件包无关。请注意,我只建议打算在REPL中使用的帮助程序。破坏全局名称空间(例如关键字函数名称空间)对我来说似乎有点麻烦。

出于良好的考虑:给定的代码均未经过测试(甚至未通过REPL传递),因此请当心。另外,我衷心建议阅读CLHS中的chapter on packages and symbols,对于这些事情,应将其视为Ultimate Source™。

答案 1 :(得分:1)

您可以使用函数import导入所需的符号:

(import '(fiveam:run! my-tests::foo))

这会将两个符号导入到您的当前包中(您也可以将其他包作为另一个参数)。然后,您可以

(run! 'foo)

如果在某些失败的尝试之后尝试执行此导入操作,则可能会进入调试器,在该调试器中,您可以选择适当的重新启动来解决相同名称的符号之间的冲突(例如“ TAKE NEW”)。