首先,请注意,这里实际上只有两个案例。'foo
被读者扩展为(quote foo)
,所以你的代码相当于
(defun testcaseexpr (thecase)
(case thecase
((quote foo) (format t "matched foo"))
(bar (format t "matched bar"))
((funk) (format t "matched funky"))))
其中,第一情况和第三情况具有相同的结构;这keys该子句的一部分是对象列表。
也许这个问题是题外话,因为它要求的是“最好的”,而这可能主要是基于意见的。我同意中提出的观点wvxvw 的回答 https://stackoverflow.com/a/19977803/1281433,但我倾向于几乎完全使用您在第三种情况中展示的样式。我这样做有几个原因:
这是最一般的形式。
这是最一般的形式。在文档中case
,我们在一个normal-clause ::= (keys form*)
keys
是键列表的指示符。这意味着像这样的子句(2 (print 'two))
相当于((2) (print 'two))
。你从来没有lose任何东西都可以通过使用列表而不是非列表来实现,但是如果您有一些带有多个对象的子句和一些带有单个对象的子句,那么所有这些子句都将具有一致的语法。例如,你可以有
(case operator
((and or) ...)
((if iff) ...)
((not) ...))
搞砸就更难了。
这使得搞乱特殊情况变得更加困难t
and otherwise
。该文档说关于keys(强调):
键——对象列表的指示符。就本案而言,
符号t
and otherwise
不能用作按键指示符。到
将这些符号本身称为键、指示符(t)
and
(otherwise)
,必须分别使用。
在实践中,一些实现会让你使用t
and otherwise
as keys在普通条款中,即使这似乎不应该被允许。例如,在 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
and otherwise
被专门叫出来,keys是一个列表指示符,这意味着nil
(一个原子和一个列表)需要一些特殊的考虑。下面的代码会产生a
or b
? (你能在不测试或检查规范的情况下判断出来吗?这种情况实际上在示例中突出显示。)
(case nil
(nil 'a)
(otherwise 'b))
它返回b
。回来a
,第一个普通子句必须是((nil) 'a)
.
结论
如果你总是确保keys是一个列表,您将:
- 最终得到看起来更一致的代码;
- 避免边缘情况错误(特别是如果您正在编写扩展为
case
); and
- 让你的意图更清晰。