21

我最近正在尝试学习一些 Lisp(Common Lisp),我想知道是否有一种方法可以像在 C 中通过枚举那样为常数命名。

我不需要枚举的完整功能集。最后,我只想拥有快速可读的代码。

我尝试过全局变量和小函数,但这总是伴随着性能下降。只需将数字插入代码总是更快。

4

3 回答 3

25

在 Lisp 中进行枚举的常规方法是使用符号。符号被嵌入(替换为指向符号表中它们的条目的指针),因此它们与整数一样快,并且与其他语言中的枚举常量一样可读。

所以你可以在 C 语言的哪个地方写:

枚举{
   苹果,
   橙,
   香蕉,
};

在 Lisp 中,您可以直接使用'apple,'orange'banana

如果你需要一个枚举类型,那么你可以定义一个deftype

(deftype fruit () '(成员苹果橙香蕉))

然后您可以使用 ,等中的类型fruitdeclaretypeptypecase并且您可以编写专门针对该类型的通用函数.

于 2009-02-23T16:45:58.757 回答
17

例如,您要命名字体大小:

(defconstant +large+ 3)
(defconstant +medium+ 2)
(defconstant +small+ 1)

您可以编写一个宏来缩短它。

上述常量定义通常仅在需要将这些数字传递给某些外部非 Lisp 代码时才编写。

否则只会使用关键字符号::large、:medium 和 :small。

你可以用 EQ 和所有使用相等测试的东西来测试它们。

(let ((size :medium))
  (ecase size
    (:small ...)
    (:medium ...)
    (:large ...)))

你也可以为它写方法:

(defmethod draw-string (message x y (size (eql :large))) ...)

如前所述,您可以定义一个集合类型:

(deftype size () '(member :small :medium :large))

然后您可以检查是否有以下内容:

(let ((my-size :medium))
  (check-type my-size size))

如果 my-size 不是 :small、:medium 或 :large 之一,则上面会发出错误信号。

您还可以使用 defclass 形式的类型:

(defclass vehicle ()
   ((width :type size :initarg :width)))

现在您将在此处创建对象:

(make-instance 'vehicle :width :large)

某些 Common Lisp 实现会检查您何时将插槽设置​​为某个非法值。

如果您现在创建类车辆的对象,插槽将是 :large、:medium 或 :small 之一。如果您在调试器、检查器或其他工具中查看对象,您将看到符号名称,而不是 1、2 或 3(或您通常使用的任何值)。

这是 Lisp 风格的一部分:尽可能使用符号名。仅在外部函数的接口代码中使用具有数字值的符号(例如调用使用枚举的外部 C 代码)。

于 2009-02-25T14:44:05.680 回答
7

枚举对于 Lisp 来说是多余的,原因是所有符号都是它们自己的标识,所以你可以只使用它们,例如:

[dsm@localhost:~]$ clisp -q
[1]> (setf x 'some) ;'
SOME
[2]> (eq x 'some) ;'
T
[3]>
于 2009-02-23T16:38:59.957 回答