为什么nil在case/ecase中无法匹配?

Ole*_*Cat 3 sbcl common-lisp

以下表格

(let ((foo nil))
  (ecase foo
    (:bar 1)
    (:baz 2)
    (nil 3)))
Run Code Online (Sandbox Code Playgroud)

抛出错误NIL fell through ECASE expression.解决方法似乎是nil用括号括起案例,如下所示:

(let ((foo nil))
  (ecase foo
    (:bar 1)
    (:baz 2)
    ((nil) 3))) ;; => 3
Run Code Online (Sandbox Code Playgroud)

但为什么第一个例子不起作用?打开的nil案件有一些特殊含义吗?

Bar*_*mar 8

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

(ecase foo
  (:bar 1) ;; match a specific symbol
  ((:baz :quux) 2)) ;; match either of these symbols
Run Code Online (Sandbox Code Playgroud)

NIL也是Lisp中的空列表,当它被用作测试时,case它是如何处理的,因此它永远不会匹配任何东西.同样,T并且OTHERWISE用于指定默认情况,因此您无法将它们作为单个项目进行匹配.要匹配其中任何一个,您需要将它们放在一个列表中.

从技术上讲,规范说明了条款中的键:

keys ---对象列表的指示符.在大小写的情况下,符号totherwise不能用作键指示符.要将这些符号自身称为键,必须分别使用指示符(t)(otherwise).

并且列表指示符的定义说:

对象列表的指示符; 也就是说,一个表示列表的对象,它是以下之一:非零原子(表示单元素列表,其元素是非零原子)或正确的列表(表示自身).

请注意,将单个原子视为单例列表的情况称为"非零原子".这允许NIL属于第二种情况,其中适当的列表表示自己.否则,将无法为空列表创建列表指示符,因为NIL这表示(NIL).

你可能会争辩说,有一个空的键列表没什么意义CASE,因为它永远不会匹配任何东西,整个子句可以省略.这种退化的情况是为了自动代码生成(例如扩展到其他宏CASE)的好处,因为它们可能产生空列表,并且这些应该与其他列表一致地处理.他们更难以使条款的生成取决于是否有任何键.