Lisp 对于case,哪种方法是表达case的最佳方法?
这些都是有效的:Lisp 对于case,哪种方法是表达case的最佳方法?,lisp,common-lisp,Lisp,Common Lisp,这些都是有效的: (defun testcaseexpr (thecase) (case thecase ('foo (format t "matched foo")) (bar (format t "matched bar")) ((funk) (format t "matched funky")))) 这三种表达方式中哪一种被认为是惯用的?也许作为一个补充,当它们显然不是相同的语法时,为什么它们都在工作呢。事实上,在其他上下文中,它们的语义完全不同。列表(fun
(defun testcaseexpr (thecase)
(case thecase
('foo (format t "matched foo"))
(bar (format t "matched bar"))
((funk) (format t "matched funky"))))
这三种表达方式中哪一种被认为是惯用的?也许作为一个补充,当它们显然不是相同的语法时,为什么它们都在工作呢。事实上,在其他上下文中,它们的语义完全不同。列表(funk)
肯定不同于引用的原子,'foo
。然而,只要输入单词foo
bar
和funk
都是一样的
除非您意外地将宏扩展为类似宏,否则永远不会使用第一个,当您有多个匹配符号(一个漏格)时,将使用第三个符号。首先,请注意,这里实际上只有两个大小写
'foo
由读者扩展为(quote foo)
,因此您的代码相当于
(defun testcaseexpr (thecase)
(case thecase
((quote foo) (format t "matched foo"))
(bar (format t "matched bar"))
((funk) (format t "matched funky"))))
其中,第一和第三壳体具有相同的结构;该子句的键部分是对象列表
也许这个问题离题了,因为它要求的是“最好的”,而这可能主要是基于观点的。我同意中提出的观点,但我倾向于使用您在第三个案例中展示的样式,几乎完全是这样。我有几个原因:
这是最普遍的形式。
这是最普遍的形式。在案例
的文档中,我们看到在普通子句中::=(键形式*)
键
是键列表的指示符。这意味着像(2(print'two))
这样的子句相当于((2)(print'two))
。通过使用列表而不是非列表,您永远不会丢失任何东西,但是如果您有一些包含多个对象的子句和一些包含单个对象的子句,那么所有这些子句的语法都是一致的。例如,你可以
(case operator
((and or) ...)
((if iff) ...)
((not) ...))
更难搞砸。
这使得处理t
和的特殊情况变得更加困难。文档中介绍了以下关键点(添加了重点):
键—对象列表的指示符在这种情况下
符号t
和否则
不能用作键指示器。至
将这些符号本身称为键、指示符(t)
和
必须分别使用(否则)
实际上,有些实现允许您使用t
和作为普通子句中的键,尽管这似乎是不允许的。例如,在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
和否则
,但键是一个列表指示符,这意味着nil
(原子和列表)需要特别考虑。以下代码是否会生成a
或b
?(在不测试或检查规范的情况下,您能说出来吗?示例中实际突出显示了这种情况。)
它返回b
。要返回a
,第一个普通子句必须是((nil)'a)
结论
如果您始终确保密钥是一个列表,您将:
最终得到外观更一致的代码李>
避免边缘大小写错误(尤其是在编写扩展到大小写的宏时);及
让你的意图更清楚
第一个是符号列表(QUOTE-FOO),所以如果案例是QUOTE,它将匹配。通常不是你想要的,但有时是——你永远不想用‘来写它以避免混淆。即使如此,在像<code>(case x(t'foo)(否则为‘bar’)这样的情况下会发生什么?
。SBCL将其扩展为(COND((EQL#:G956'T)NIL'FOO)(T NIL'BAR))
。看起来t
实际上是作为一个符号进行比较的。这就是为什么我通常更喜欢第三个选项。@Xach我对第一个选项所说的话具有讽刺意味,暗示这很可能是一个错误(可能是一个疏忽),而不是有人想故意放在他们的程序中。第三个选项不仅仅是为了“多个匹配符号”。符号t
,否则
和nil
都需要它。它不用于不合格案例;漏检案例由t
或标记,否则
@JoshuaTaylor用于漏检案例。失败案例是一个,其中许多案例中只有一个需要匹配。您所指的被称为默认值
case.>(testcaseexpr'quote)->匹配foo@RainerJoswig这很有趣,为什么会这样foo=(quote foo)->quote匹配quote。引用case子句项是没有意义的。
(case nil
(nil 'a)
(otherwise 'b))