我尝试为cond
和case
创建一个示例,并提出了 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 显然不应该做它应该做的事情。
任何提示为什么这两个案件都像他们一样?
答案 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 answer到How to group any consecutive numbers or items of a given series的另一天使用了它。