使用带有一个或多个符号的 case

Using case with one or more symbols

我仍然无法真正掌握case。我了解到您提供的符号在内部使用 eq 进行比较,但我不确定我是否正确理解了一件事:

我看到我会写,例如:

(case n
  (23 'foo)
  (42 'bar)
  (otherwise 'something-else))

我也会写:

(case n
  ((23 42) 'foo-or-bar)
  (otherwise 'something-else))

所以我是对的,如果我指定一个 case 的列表,然后检查变量 n 是否匹配 列表的一个元素 ,但是如果我指定一个值,那么case直接匹配这个值?

换句话说:如果我只有一个值,可以使用非列表版本吗?

键在概念上是键的列表

从概念上讲,每个子句都使用一个键列表。测试密钥将与密钥列表中的每个密钥进行比较。

密钥列表 (foo bar baz) 恰好是 (foo bar baz)

foo 被认为表示 (foo)。它有助于编写更短的代码。

(case x
  (foo       41)
  ((bar baz) 42)))

T和OTHERWISE除外

请注意,otherwise(otherwise)t(t) 是例外情况。如果要匹配符号,则需要写(otherwise)

(case 'otherwise
  ((otherwise) 'the-symbol-otherwise)
  (otherwise   'the-otherwise-clause))

EQ 与 EQL

另请注意,Common Lisp 中的大多数比较默认使用 EQL 而不是 EQEQ是指针相等,EQL也适用于数字和字符。

这不是直接回答你的问题,但我们可以使用 lisp 看看它在做什么。

如果您在 CLHS 页面中查找 case,我们可以看到它是一个宏,这意味着我们可以对其进行宏扩展以查看它变成什么

那么让我们以第一个例子为例

(case n
  (23 'foo)
  (42 'bar)
  (otherwise 'something-else))

这扩展为

(LET ((#:G1246 N))
  (COND ((EQL #:G1246 '23) NIL 'FOO)
        ((EQL #:G1246 '42) NIL 'BAR)
        (T NIL 'SOMETHING-ELSE)))

#:G1246 是一个 gensym,我们可以将其视为 lisp 保证唯一的符号。我暂时将其重命名为 tmp

(let ((tmp n))
  (cond ((eql tmp '23) nil 'foo)
        ((eql tmp '42) nil 'bar)
        (t nil 'something-else)))

另外 cond 是一个宏..让我们看看它是如何扩展的(我已经像上面那样简化了它)

(let ((tmp n))
  (if (eql tmp '23)
      'foo
      (if (eql tmp '42)
          'bar
          'something-else)))

现在我们可以看到所有的逻辑了

现在展开下一个

(case n
  ((23 42) 'foo-or-bar)
  (otherwise 'something-else))

变成

(let ((tmp n))
  (if (or (eql tmp '23) (eql tmp '42))
      'foo-or-bar
      'something-else))

Macroexpand 太有用了。希望这有帮助