【问题标题】:Add package name to symbol将包名称添加到符号
【发布时间】:2019-02-06 17:49:12
【问题描述】:

这是特定上下文中的一般符号问题。我想我需要回答的问题是:给定一个包含符号 'foo 的参数,我如何操作符号的包部分,使其 eql 到 'package:foo?

更具体的上下文:有一个单元测试包,fiveam,它将测试函数存储在哈希表 (it.bese.fiveam::*tests) 中,其中带有像 package::test-name 这样的键散列到包含测试的对象。要运行测试,通常将一个作为哈希键的符号传递给run! 函数:(run! 'package::test-name。如果我从定义测试的tests 包中调用run!,我可以在(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 不同。

有问题的哈希表使用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,但我似乎无法得到任何 eql 'foo:bar,给定 'bar

注意:我并不关心为包名输入额外的几个字母。我在这里真正想做的是提高我对范围和指定符号的理解。

谢谢。

【问题讨论】:

  • (find-symbol (symbol-name 'bar) :foo) 将在名为 FOO 的包中返回名为 BAR 的符号,如果这样的符号被实习在那里。

标签: common-lisp symbols


【解决方案1】:

首先,我假设您知道,您可以使用扩展命令 in-package 切换到 SLIME REPL 中的 test 包(即,在 REPL 提示符下,键入逗号 ,,然后in-package,系统会提示你输入包名),这样切换包对你来说简直不方便。

一种解决方案可能是在您当前使用的任何包中引入一个小辅助函数:

(defun run-test (name &optional (package 'test))
   (funcall (find-symbol #.(symbol-name 'run!) package)
            (find-symbol (symbol-name name) package)))

或更简单(假设run! 的定义来自fiveam 包,通过: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)))

现在你可以把它称为

(:run 'check-everything)

不管当前的包是什么。请注意,我只向打算在 REPL 中使用的助手推荐这个。像这样破坏全局命名空间(如关键字函数命名空间)对我来说有点可疑。

只是为了衡量:给出的代码都没有经过测试(甚至没有通过 REPL),所以要小心。另外,我衷心推荐阅读 CLHS 中的 chapter on packages and symbols,它应该被视为这些东西的终极来源™。

【讨论】:

  • 这理清了我的一些想法。谢谢。
【解决方案2】:

您可以使用函数import 来导入您想要的符号:

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

这会将两个符号导入您的当前包(您也可以提供不同的包作为另一个参数)。然后就可以了

(run! 'foo)

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

【讨论】:

    猜你喜欢
    • 2013-03-13
    • 2023-01-11
    • 2012-08-10
    • 2022-11-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-06-23
    • 2014-01-22
    相关资源
    最近更新 更多