【问题标题】:Questions on case案例问题
【发布时间】:2014-06-05 13:17:17
【问题描述】:

我尝试为 condcase 创建一个示例,并提出了 fizz buzz 问题的简单实现(有关详细信息,请参阅 Wikipedia)。

我的初始版本是:

(defun is-fizz-buzz (n)
  (cond ((and (zerop (mod n 3)) (zerop (mod n 5))) 'fizz-buzz)
        ((zerop (mod n 3)) 'fizz)
        ((zerop (mod n 5)) 'buzz)
        (t n)))

(defun fizz-buzz (n)
  (princ (case (is-fizz-buzz n)
               ('fizz "Fizz!")
               ('buzz "Buzz!")
               ('fizz-buzz "Fizz-Buzz!")
               (otherwise n))))

现在,@RainerJoswig 向我指出我的case 是错误的。我很惊讶,因为它应该起作用了,但是由于他在 Lisp 方面的经验比我方式多,他很可能是对的(事实上,他是)。

我试图阅读和理解http://clhs.lisp.se/Body/m_case_.htm,但现在我的问题多于答案。由于我不想讨论 140 个字符的内容,而且它可能对其他 Lisp 初学者有所帮助,所以我想在这里发布我的问题。

基本上case的语法描述为

case keyform {normal-clause}* [otherwise-clause] => result*

keyform 很简单:它是一种经过评估以获取正在测试的密钥的表单。肯定的

(is-fizz-buzz n)

是一种形式,它返回一个符号,因此一切看起来都很好。此外,正如我从 Land of Lisp 中了解到的,case 在内部与 eq 一起使用,并且由于 eq 用于比较符号,这也应该没问题。

现在,normal-clause:这被定义为

(keys form*)

form* 还是很简单,它只是一些形式,基本上是隐含的progn。对于keys,它告诉我这是一个对象列表的指示符。呸。我的问题从这里开始……

This document 告诉我指示符是:

一个表示另一个对象的对象。

嗯。由于case 正在与eq 合作,我本以为我需要在这里给出一个符号。为什么keyslist 对象的指示符,而不是单个对象?我可以在一个分支中比较多个符号吗?我想这里的问题归结为没有真正理解designator 的含义,但也许有人可以帮助我并将我推向正确的方向。你会如何解释 Lisp 中的指示符是什么?

然后,我开始使用代码,我注意到如果我删除 ' 字符,事情仍然有效。代码

(defun fizz-buzz (n)
  (princ (case (is-fizz-buzz n)
               (fizz "Fizz!")
               (buzz "Buzz!")
               (fizz-buzz "Fizz-Buzz!")
               (otherwise n))))

产生与上面的代码完全相同的结果。为什么是这样?我什至根本没想到这是可执行的,因为(fizz "Fizz!") 对我来说看起来像是fizz 的函数调用,它不存在。为什么会这样?

然后,最后一个问题是,当我跑步时

(case 'quote ('foo 1) ('bar 2))

它返回1(这似乎不合逻辑,我原以为nil)。如果我将其更改为

(case 'quote ('foo 1) ('bar 2) (otherwise 3))

它仍然返回1,而不是我现在所期望的3。从文档中我不明白为什么我的 otherwise-clause 显然不应该做什么。

任何提示为什么这两种情况的行为如此?

【问题讨论】:

  • 其元素是那个非零原子)或一个适当的列表(表示它自己)。”因此符号fizz 和列表(fizz) 都表示列表(fizz),列表@ 987654358@ 指定自己等

标签: lisp common-lisp


【解决方案1】:

CASE 使用 EQL 而不是 EQEQL 在大多数情况下是默认比较。因此大小写适用于身份、数字和字符。

不评估密钥形式。因此,引用对象是没有意义的。引用意味着停止评估。但是没有评估 -> 没有报价。

keyform 可以是单个项目,也可以是项目列表

(case foo
  (bar 'it-is-bar)
  ((1 2) 'one-or-two)
  (1     'one))
  ((apple banana orange) 'either-apple-banana-or-orange)))

这也意味着左边一个是常数,没有变量。上面的bar 是符号bar,而不是变量bar

问题:

 (case 'quote
   ('foo 'foo-or-quote))

因为真的是

 (case 'quote
   ((quote foo) 'foo-or-quote))

【讨论】:

  • 感谢您的解释,现在更有意义了:-))。顺便说一句,第 58 页的 Land of Lisp 声称 case 使用 eq(这并不意味着这一定是正确的 ;-))。
  • @Yes:ANSI 标准在一处提到EQL 是默认比较。不幸的是,这在描述MEMBERPOSITIONCASE、... 'Land of Lisp' 的页面中并不容易看到这里是错误的。也许这本书的勘误页中已经提到过?
【解决方案2】:

好的,我能够自己解决一些问题,主要是通过阅读this question and the according answers。这向我解释了为什么我的 else-clause 不起作用,也解释了原因

(case 'quote ('foo 1) ('bar 2))

正在按原样进行评估。所以我猜我的fizz-buzz函数至少应该是:

(defun fizz-buzz (n)
  (princ (case (is-fizz-buzz n)
               (fizz "Fizz!")
               (buzz "Buzz!")
               (fizz-buzz "Fizz-Buzz!")
               (otherwise n))))

更新

好的,现在在多次重新阅读上述问题和答案之后,我想我终于明白了对象列表的含义,因此我的代码应该是:

(defun fizz-buzz (n)
  (princ (case (is-fizz-buzz n)
               ((fizz) "Fizz!")
               ((buzz) "Buzz!")
               ((fizz-buzz) "Fizz-Buzz!")
               (otherwise n))))

【讨论】:

  • 看起来不错,但是您没有必须将它们放入列表中,因为casekeys 部分是“指示符对于对象列表”,而list designator 是“表示列表的对象,它是以下之一:非零原子(表示其元素是该非零原子的单例列表)或适当的列表(表示自己)。”因此符号fizz 和列表(fizz) 都指定了列表(fizz)
  • 感谢您指出这一点:-)。我决定这样做是为了保持风格一致,无论我的列表是由单个项目还是由多个项目组成。此外,这有助于区分 otherwise(otherwise)。但是,很高兴知道:-)
  • 其实,我也只是在an answer 中对其进行了扩展。很高兴听到它有帮助。 :)
【解决方案3】:

对于keys,它告诉我这是一个列表的指示符 对象。呸。我的问题从这里开始……

This document 告诉我一个指示符是:

一个表示另一个对象的对象。

嗯。由于case 正在与eq 合作,我本以为我需要 在这里给出一个符号。为什么keys列表的指示符 对象,而不是单个对象?我可以有多个符号吗? 可以在单个分支中进行比较吗?我想问题来了 甚至没有真正理解designator 的含义,但是 也许有人可以帮助我并将我推向正确的方向。如何 你能解释一下 Lisp 中的指示符是什么吗?

我最初将此作为评论发布,但我认为这可能是一个足够的答案,如果它是问题中的核心问题,所以我将其添加为答案。 p>

您需要的文本在 HyperSpec 中。 1.4.1.5 Designators笔记

由“ 指示符”表示的对象的特定性质 或“ 的指示符”可以在词汇表条目中找到 对于“ 指示符。”

列表指示符的词汇表条目是您所需要的:

list designatorn。对象列表的指示符;那是, 一个表示列表的对象,它是以下之一:非零原子 (表示一个单例列表,其元素是那个非零原子)或一个正确的列表(表示它自己)。

因此符号fizz(一个非零原子)和列表(fizz)都表示列表(fizz),列表(quote fizz)(通常缩写为'fizz)表示它自己,等等

一旦您了解了列表指示符,它们就会是一个非常方便的概念,可以在您自己的代码中使用。只需记录您接受列表指示符,然后使用(if (listp x) x (list x)) 进行转换,您就可以开展业务了。它非常有用,您会发现它的用途比您预期的要多。例如,我前几天在an answerHow to group any consecutive numbers or items of a given series 中使用它。

【讨论】:

    猜你喜欢
    • 2016-08-04
    • 2019-01-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-10-10
    • 1970-01-01
    相关资源
    最近更新 更多