有案例,这是表达案件的最佳方法吗?

joh*_*ers 5 lisp common-lisp

这一切都有效:

(defun testcaseexpr (thecase)
  (case thecase
    ('foo (format t "matched foo"))
    (bar (format t "matched bar"))
    ((funk) (format t "matched funky"))))
Run Code Online (Sandbox Code Playgroud)

这三种表达中的哪一种被认为是惯用的方式?也许作为一个侧面点,为什么它们都在工作,显然它们的语法不一样.实际上在其他情况下,它们完全具有不同的语义.列表(funk)肯定与引用的原子不同,'foo.然而,只是传递单词foo barfunk所有工作相同.

Jos*_*lor 7

首先,请注意,您实际上只有两个案例. 'foo被读者扩展为(quote foo),所以你的代码相当于

(defun testcaseexpr (thecase)
  (case thecase
    ((quote foo) (format t "matched foo"))
    (bar         (format t "matched bar"))
    ((funk)      (format t "matched funky"))))
Run Code Online (Sandbox Code Playgroud)

其中第一和第三种情况具有相同的结构; 子句的部分是对象列表.

也许这个问题是偏离主题的,因为它要求的是"最好的",而这可能主要是基于意见的.我同意wvxvw的答案所提出的观点,但我倾向于使用你在第三种情况下几乎完全展示的风格.我有几个理由:

这是最一般的形式.

这是最一般的形式.在文档中case,我们读到了一个normal-clause ::= (keys form*) keys是一个键列表的指示符.这意味着类似的子句(2 (print 'two))相当于((2) (print 'two)).通过使用列表而不是非列表,您永远不会丢失任何东西,但如果您有一些包含多个对象的子句,而某些子句具有单个对象,则您将拥有所有这些对象的一致语法.例如,你可以拥有

(case operator
  ((and or) ...)
  ((if iff) ...)
  ((not)    ...))
Run Code Online (Sandbox Code Playgroud)

陷入困境更加困难.

这使得它更难陷入困境的特殊情况totherwise.文档说明了关键(强调添加):

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

在实践中,一些实现将允许您在正常子句中使用totherwise作为,即使看起来不应该允许这样做.例如,在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)))
Run Code Online (Sandbox Code Playgroud)

使用显式列表可以消除您尝试执行的操作的任何歧义.即使t并且otherwise被特别指出,是列表指示符,这意味着nil(原子和列表)需要一些特殊的考虑.以下代码是否会产生ab?(您可以在不测试或检查规范的情况下判断吗?这个案例实际上是在示例中突出显示的.)

(case nil
  (nil 'a)
  (otherwise 'b))
Run Code Online (Sandbox Code Playgroud)

它回来了b.要返回a,第一个正常条款必须是((nil) 'a).

结论

如果您始终确保密钥是列表,您将:

  1. 最终得到更一致的代码;
  2. 避免边缘错误(特别是如果你正在编写扩展的宏case); 和
  3. 让你的意图更清晰.