首先,请注意这里实际上只有两个案例。 'foo
由读者扩展为(quote foo)
,因此您的代码相当于
(defun testcaseexpr (thecase)
(case thecase
((quote foo) (format t "matched foo"))
(bar (format t "matched bar"))
((funk) (format t "matched funky"))))
其中,第一种情况和第三种情况具有相同的结构;子句的键部分是对象列表。
也许这个问题是题外话,因为它要求的是“最好的”,而这可能主要是基于意见的。我同意wvxvw 的回答中提出的观点,但我倾向于几乎完全使用您在第三种情况下展示的风格。我有几个原因:
这是最一般的形式。
这是最一般的形式。在 的文档中case
,我们读到 annormal-clause ::= (keys form*)
keys
是键列表的指示符。这意味着子句 like(2 (print 'two))
等价于((2) (print 'two))
. 通过使用列表而不是非列表,您永远不会丢失任何东西,但是如果您有一些包含多个对象的子句和一些包含单个对象的子句,那么您将拥有所有它们的一致语法。例如,你可以有
(case operator
((and or) ...)
((if iff) ...)
((not) ...))
更难搞砸了。
这使得混淆 和 的特殊情况变得更加t
困难otherwise
。该文档说明了以下键(强调添加):
键——对象列表的指示符。在 case 的情况下,符号t
和otherwise
不得用作键指示符。要将这些符号本身称为键,必须分别使用指示符(t)
和
。(otherwise)
在实践中,一些实现将允许您在普通子句中使用t
和otherwise
作为键,即使看起来不应该允许这样做。例如,在 SBCL 中:
CL-USER> (macroexpand-1 '(case keyform
(otherwise 'a)
(otherwise 'b)))
(LET ((#:G962 KEYFORM))
(DECLARE (IGNORABLE #:G962))
(COND ((EQL #:G962 'OTHERWISE) NIL 'A)
(T NIL 'B)))
使用显式列表可以消除您正在尝试做的任何歧义。尽管t
和otherwise
被特别指出,keys是一个列表指示符,这意味着nil
(一个原子和一个列表)需要一些特殊的考虑。下面的代码会产生a
orb
吗?(你能在不测试或检查规范的情况下判断吗?这种情况实际上在示例中突出显示。)
(case nil
(nil 'a)
(otherwise 'b))
它返回b
。要返回a
,第一个正常子句必须是((nil) 'a)
。
结论
如果您始终确保键是一个列表,您将:
- 最终得到更一致的代码;
- 避免边缘情况错误(特别是如果您正在编写扩展为 的宏
case
);和
- 让你的意图更清楚。