为什么在case/ecase中匹配不到nil?

Why nil cannot be matched in case/ecase?

如下形式

(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 大小写是否有特殊含义?

case 中的每个子句都可以匹配单个项目或项目列表,例如你可以写:

(ecase foo
  (:bar 1) ;; match a specific symbol
  ((:baz :quux) 2)) ;; match either of these symbols

NIL 也是 Lisp 中的空列表,当它用作 case 中的测试时,它就是这样处理的,所以它永远不会匹配任何东西。同样,TOTHERWISE 用于指定默认大小写,因此您不能将它们作为单个项进行匹配。要匹配其中任何一个,您需要将它们放入列表中。

从技术上讲,specification 对子句中的键进行了说明:

keys---a designator for a list of objects. In the case of case, the symbols t and otherwise may not be used as the keys designator. To refer to these symbols by themselves as keys, the designators (t) and (otherwise), respectively, must be used instead.

list designator 的定义说:

a designator for a list of objects; that is, an object that denotes a list and that is one of: a non-nil atom (denoting a singleton list whose element is that non-nil atom) or a proper list (denoting itself).

请注意,单个原子被视为单个列表的情况表示 "non-nil atom"。这允许 NIL 属于第二种情况,其中一个适当的列表表示它自己。否则,将无法为空列表创建列表指示符,因为 NIL 将表示 (NIL).

您可能会争辩说,在 CASE 中有一个空的键列表没有多大意义,因为它永远不会匹配任何内容,并且可以省略整个子句。这种退化的情况是为了自动代码生成的好处(例如扩展为 CASE 的其他宏),因为它们可能会产生空列表,并且这些应该与其他列表一致地处理。他们将更难使子句的生成以是否有任何键为条件。