以下形式
(let ((foo nil))
(ecase foo
(:bar 1)
(:baz 2)
(nil 3)))
抛出错误NIL fell through ECASE expression
。解决方法似乎是将nil
大小写用括号括起来,如下所示:
(let ((foo nil))
(ecase foo
(:bar 1)
(:baz 2)
((nil) 3))) ;; => 3
但是为什么第一个示例不起作用?展开的nil
大小写是否有特殊含义?
答案 0 :(得分:8)
case
中的每个子句可以匹配单个项目或项目列表,例如你可以这样写:
(ecase foo
(:bar 1) ;; match a specific symbol
((:baz :quux) 2)) ;; match either of these symbols
NIL
也是Lisp中的空列表,在case
中用作测试时,这就是它的处理方式,因此它永远不会匹配任何内容。同样,T
和OTHERWISE
用于指定默认大小写,因此您不能将它们作为单个项目进行匹配。要匹配其中任何一个,您需要将它们放在列表中。
从技术上讲,specification谈到子句中的键:
键 ---对象列表的指示符。在这种情况下,符号
t
和otherwise
可能不用作键指示符。要将这些符号本身称为键,必须分别使用指示符(t)
和(otherwise)
。
list designator的定义是:
一个对象列表的指示符;也就是说,一个对象表示一个列表,并且是下列对象之一:非零原子(表示元素为该非零原子的单例列表)或适当的列表(表示自身)。
请注意,将单个原子视为单例列表的情况为“非零原子”。这允许NIL
进入第二种情况,其中一个适当的列表表示了自己。否则,将无法为空列表创建列表指示符,因为NIL
将表示(NIL)
。
您可能会争辩说,CASE
中的键列表为空没有太大意义,因为它永远不会匹配任何内容,整个子句可以省略。这种退化的情况是为了自动代码生成(例如,其他扩展为CASE
的宏)的好处,因为它们可能会产生空列表,并且应该与其他列表保持一致。对于他们来说,使子句的生成以是否有任何键为条件更加困难。