Clozure Common Lisp 中的 Sharpsign dot 宏。

3

我想像"Common Lisp Recipes"书中建议的那样,在case宏中使用常量变量。

  • 10-2. 使用常量变量作为CASE宏中的键

不幸的是,在Clozure CL中无法实现。

(defpackage #:foo
  (:use #:cl))

(in-package #:foo)

(defconstant +one+ 1)
(defconstant +two+ 2)

(defun lol (gg)
  (ecase gg
    (#.+one+ :one)
    (#.+two+ :two)))

这段代码无法加载。

 Unbound variable: FOO::+ONE+
   [Condition of type UNBOUND-VARIABLE]

Restarts:
 0: [CONTINUE] Retry getting the value of FOO::+ONE+.
 1: [USE-VALUE] Specify a value of FOO::+ONE+ to use this time.
 2: [STORE-VALUE] Specify a value of FOO::+ONE+ to store and use.

这段代码在SBCL中可以正常运行,为什么在CCL中不能正常工作?

我正在使用macOS上的64位Clozure CL 1.12。

1个回答

4

CCL会高兴地为我加载这个文件的源代码,我相信任何Common Lisp实现都应该这么做。

但是它不会(我相信任何CL也不会)对其进行编译。它不会将其编译,因为defconstant不会在编译时定义常量。这意味着,在编译lol时,它引用了一个尚未定义的变量。

如果你想像这样处理常量,你需要确保变量在编译时已经被定义。有两种方法可以做到这一点:

首先,你可以添加适当的eval-when,然后相关的源代码块将会是:

(eval-when (:compile-toplevel :load-toplevel :execute)
  (defconstant +one+ 1)
  (defconstant +two+ 2))

(defun lol (gg)
  (ecase gg
    (#.+one+ :one)
    (#.+two+ :two)))

第二种方法是将常量放在自己的文件中,该文件在使用之前已编译并加载。通常这是通过像ASDF这样的系统定义工具来管理的。
注:我认为任何CL都应该能够加载源代码文件,因为我认为即使是仅编译器实现,在加载源代码文件时也需要逐个读取它们:换句话说,我不认为将`(load "foo.lisp")`转换为`(load (compile-file "foo.lisp"))`合法。但是我可能错了:我已经很久没有详细阅读规范了。

你是对的。我正在尝试编译这个文件。不知何故,在SBCL中它可以很好地编译。 - Maris Orbidans
1
一个实现可以选择在编译时、加载时或两者同时评估值形式。 - coredump
@coredump:是的,正是如此(这就是为什么有声明值必须始终相同的语句)。这使得编译器可以通过在编译环境中存储该值来将对常量的引用视为文字。但编译器实际上并不在编译时定义变量,就像它不应该在编译时定义函数一样。当然,宏是一个更强的例子。 - user5920214

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接