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

时间:2013-11-14 12:30:13

标签: lisp common-lisp

这些都有效:

(defun testcaseexpr (thecase)
  (case thecase
    ('foo (format t "matched foo"))
    (bar (format t "matched bar"))
    ((funk) (format t "matched funky"))))

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

2 个答案:

答案 0 :(得分: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"))))

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

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

这是最常见的形式。

这是最常见的形式。在case的文档中,我们读到normal-clause ::= (keys form*) keys中有一个键列表的指示符。这意味着像(2 (print 'two))这样的子句等同于((2) (print 'two))。你永远不会通过使用列表而不是非列表来丢失任何东西,但是如果你有一些带有多个对象的子句和一些带有单个对象的子句,你将拥有所有这些对象的一致语法。例如,你可以拥有

(case operator
  ((and or) ...)
  ((if iff) ...)
  ((not)    ...))

很难搞砸。

这使得totherwise的特殊情况变得更加困难。文档说明了 keys (强调添加):

  

keys - 对象列表的指示符。 在案件的情况下,   符号totherwise不得用作键指示符。 To   将这些符号本身称为键,指示符(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)))

使用显式列表可以消除您尝试执行的操作的任何歧义。即使专门调出totherwise keys 也是列表指示符,这意味着nil(原子和列表)需要特别考虑。以下代码会生成a还是b? (您可以在不测试或检查规范的情况下判断吗?这个案例实际上在示例中突出显示。)

(case nil
  (nil 'a)
  (otherwise 'b))

返回b。要返回a,第一个normal-clause必须是((nil) 'a)

结论

如果您始终确保是一个列表,您将:

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

答案 1 :(得分:0)

第二个:)

首先从不使用,除非你意外地将宏扩展成类似的东西,并且当你有一个匹配符号(一个落空案例)时使用第三个。