关于案件的问题

时间:2014-06-05 13:17:18

标签: lisp common-lisp

我尝试为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)

是一个表单,它返回一个符号,因此一切看起来都很好。另外,正如我从 Lisp的土地中学到的那样,case内部使用eq,而eq用于比较符号,这也应该没问题

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

(keys form*)

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

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

  

表示另一个对象的对象。

嗯。由于case正在与eq合作,我原本以为我需要在这里给出一个符号。为什么keys列表对象的指定符,而不是单个对象?我可以在一个分支中使用多个符号进行比较吗?我想这里的问题归结为没有真正理解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 显然不应该做它应该做的事情。

任何提示为什么这两个案件都像他们一样?

3 个答案:

答案 0 :(得分:5)

CASE使用EQL而不是EQ。在大多数情况下,EQL是默认比较。因此,案例适用于身份,数字和人物。

不评估键形式。引用对象是没有意义的。引用意味着停止评估。但是没有评估 - >没有引用。

键形式可以是单个项目,也可以是项目列表

(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))

答案 1 :(得分:1)

好的,我自己能够找出一些问题,主要是通过阅读this question and the according answers。这向我解释了为什么我的其他条款没有起作用,这也解释了为什么

(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))))

答案 2 :(得分:1)

  

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

     

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

     
    

表示另一个对象的对象。

  
     

嗯。由于case正在使用eq,我原本预计会需要   在这里给出一个符号。为什么keys列表的指示符   对象,而不是单个对象?我可以有多个符号吗?   可以在一个分支中进行比较?我想这里的问题来了   而不是真正理解designator的意思,但是   也许有人可以帮助我,并把我推向正确的方向。怎么样   你会解释Lisp中的指示符是什么吗?

我最初将此作为评论发布,但我认为这可能是一个充分的答案,如果它是问题中的核心问题,那么我将其添加为答案。

您需要的文字在HyperSpec中。 1.4.1.5 Designators注意事项

  

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

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

  

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

因此,符号fizz(非零原子)和列表(fizz)都指定列表(fizz),列表(quote fizz)(通常缩写为{{1} }})指定自己等等。

一旦你了解了列表指示符,它们就是在你自己的代码中使用的非常方便的概念。只记录您接受列表指示符,然后使用'fizz进行转换,即可开展业务。它非常有用,你会发现它的用途超出你的预期。例如,我在an answerHow to group any consecutive numbers or items of a given series的另一天使用了它。