我最近正在尝试学习一些 Lisp(Common Lisp),我想知道是否有一种方法可以像在 C 中通过枚举那样为常数命名。
我不需要枚举的完整功能集。最后,我只想拥有快速且可读的代码。
我尝试过全局变量和小函数,但这总是伴随着性能下降。只需将数字插入代码总是更快。
我最近正在尝试学习一些 Lisp(Common Lisp),我想知道是否有一种方法可以像在 C 中通过枚举那样为常数命名。
我不需要枚举的完整功能集。最后,我只想拥有快速且可读的代码。
我尝试过全局变量和小函数,但这总是伴随着性能下降。只需将数字插入代码总是更快。
在 Lisp 中进行枚举的常规方法是使用符号。符号被嵌入(替换为指向符号表中它们的条目的指针),因此它们与整数一样快,并且与其他语言中的枚举常量一样可读。
所以你可以在 C 语言的哪个地方写:
枚举{ 苹果, 橙, 香蕉, };
在 Lisp 中,您可以直接使用'apple
,'orange
和'banana
。
如果你需要一个枚举类型,那么你可以定义一个deftype
:
(deftype fruit () '(成员苹果橙香蕉))
然后您可以使用 ,等中的类型fruit
,declare
typep
typecase
并且您可以编写专门针对该类型的通用函数.
例如,您要命名字体大小:
(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 代码)。
枚举对于 Lisp 来说是多余的,原因是所有符号都是它们自己的标识,所以你可以只使用它们,例如:
[dsm@localhost:~]$ clisp -q
[1]> (setf x 'some) ;'
SOME
[2]> (eq x 'some) ;'
T
[3]>