【问题标题】:Parens 'options' with the cond operator?使用 cond 运算符的 Parens 'options'?
【发布时间】:2019-07-09 14:09:14
【问题描述】:

我决定整理以下函数。这个想法是它使用cond,但它也包含ifs,这使得认知处理变得困难。这是 ansi common lisp 一书中的 bst 代码。

(defun percolate (bst)                      
  (cond ((null (node-l bst))                
         (if (null (node-r bst))
             nil
             (rperc bst)))
        ((null (node-r bst)) (lperc bst))
        (t (if (zerop (random 2))
               (lperc bst)
               (rperc bst)))))

我的想法是通过在cond 中添加更多案例来删除 ifs,然后用左边的原因很好地证明整个事情,右边的效果。 这是我想出的:

(defun percolate (bst)                      ; [6,7,7a]
  (cond (((and (null (node-l bst)) (null (node-r bst)))  nil)
         ((null (node-l bst))                            (rperc bst))
         ((null (node-r bst))                            (lperc bst))
         (t                                              (if (zerop (random 2))
                                                             (lperc bst)
                                                             (rperc bst))))))

但是,这会产生错误

*** - SYSTEM::%EXPAND-FORM: (AND (NULL (NODE-L BST)) (NULL (NODE-R BST))) should be a
      lambda expression

我可以在堆栈溢出上找到其他有关此问题的帖子,例如here,但我仍然不明白。 cond 是否以某种方式偏离了正常的 lisp 语法?我想确定我所做的假设是错误的。

为了记录,下面的代码被解释器接受了,但显然我们不想这样写。

(defun percolate (bst)                      ; [6,7,7a]
  (let ((both-null (and (null (node-l bst)) (null (node-r bst))))
        (l-null    (null (node-l bst)))
        (r-null    (null (node-r bst))))
  (cond ((both-null                                              nil)
         (l-null                                         (rperc bst))
         (r-null                                         (lperc bst))
         (t                                              (if (zerop (random 2))
                                                             (lperc bst)
                                                             (rperc bst)))))))

【问题讨论】:

  • 您可能想考虑换一种方式:(random-elt (delete nil (vector left right)))
  • 我认为他的渗滤液中的random 位是错误的,正如书中勘误表中所解释的那样。有人评论说“删除的内部节点需要被左子树中的最大节点或右子树中的最小节点替换,而你的函数不这样做”......但我还没有完全理解还没有......但我的帖子只是关于语法...... :)

标签: common-lisp


【解决方案1】:

cond 语法:

cond {clause}*

clause::= (test-form form*) 

例子

(cond (var)
      (var a b c)
      (var (f a) (f b) (f c))
      ((and var-a var-b) (f c) (f d))
      ((f a) b (f c) d)
      (t (f a) (f b) (f c)))

意思:

  • 零个或多个子句。
  • 每个子句都在一个列表中,以一个测试表格开头,然后是零个或多个表格

let 语法:

let ({var | (var [init-form])}*)
  declaration*
  form*

意思:

  • 一个列表中有零个或多个可变子句
  • 每个变量子句要么是一个变量,要么是一个变量列表和可选的 init-form
  • 变量子句列表后跟可选的零个或多个声明,然后是零个或多个主体形式

例子:

(let ()
   )

(let (a)
  a)

(let (a
      (b t))
  (and a b))

(let ((a t)
      (b (not t)))
  (or a b))

【讨论】:

    【解决方案2】:

    我在写这篇文章的过程中发现了一个可能的答案。

    我仍然会发帖,因为很高兴收到任何进一步的澄清和讨论。

    让我们首先陈述我们所知道的。使用 let,需要有一对括号来包含所有分配的单个词法变量:

    (let ((both-null (and (null (node-l bst)) (null (node-r bst))))
          (l-null    (null (node-l bst)))
          (r-null    (null (node-r bst))))
    
    

    但假设 cond 遵循相同的模式(我曾假设)是错误的。

    Cond 工作得很好没有周围的括号。 解释器接受以下表格:

    (defun percolate (bst)                      ; [6,7,7a]
      (cond ((and (null (node-l bst)) (null (node-r bst)))  nil)
            ((null (node-l bst))                            (rperc bst))
            ((null (node-r bst))                            (lperc bst))
            (t                                              (if (zerop (random 2))
                                                                (lperc bst)
                                                                (rperc bst)))))
    

    但是(这是让我很生气的部分),事实证明 解释器会接受额外的括号,如果有人无意中复制了 let 形式的语法。

    ... 除非! - cond 表达式中的第一个形式本身是复合形式,即不是原子。

    我非常感谢社区提供的任何其他答案,这些答案可以进一步解释和/或扩展或更正我在此处找到的内容。

    更新:我对这些表单有效性的测试是将它们粘贴到 REPL 中。事实证明这还不够,您必须按照 Rainer Joswig 在 cmets 中的说明进行编译。

    所以,一些结论

    • 确保记住 cond 不遵循 let 的语法模型
    • 要小心,因为如果你陷入这种假设,你可以暂时摆脱它,然后由于上面给出的原因突然它就不起作用了。
    • 如果您一直在用额外的括号编写 cond,请正确练习并学会在没有括号的情况下识别它。

    【讨论】:

    • “接受”到底是什么意思?额外的括号不起作用。括号在 Lisp 中是有意义的。你不能只是把它们扔掉,然后认为没关系。
    • 问题中最后的代码块不起作用。
    • 通过'work',我的意思是我可以将它粘贴到CLisp REPL中,它会打印回函数名的字符串,这就是我所说的'accept',从中我推断它在语法上是有效的。我现在刚刚粘贴它并在我的案例中得到了这个结果。 “工作”是什么意思?
    • 这不是你想的那样。将其粘贴并编译。 [2]>(编译 *)警告:在 PERCOLATE 中:未定义函数 NODE-L 警告:在 PERCOLATE 中:未定义函数 NODE-R 警告:在 PERCOLATE 中:未定义函数 NODE-L 警告:在 PERCOLATE 中:函数NODE-R 未定义警告:在 PERCOLATE 中:未定义函数 BOTH-NULL
    • 正如 Svante 所说,括号在 Lisp 中很重要。比如这个()不是'nothing',它是nil——(eq () nil) => T,而(())会在REPL提示符处抛出错误,因为它等价于(nil),而nil没有定义为函数。此外,cond 是一个宏,而let 是一个特殊运算符(请参阅clhs.lisp.se/Body/m_cond.htmlispworks.com/documentation/HyperSpec/Body/s_let_l.htm)。特殊运算符是“特殊的”,因为它们使用特殊的评估规则,并且宏评估其参数的方式与 Lisp 函数不同。
    猜你喜欢
    • 1970-01-01
    • 2020-11-27
    • 1970-01-01
    • 1970-01-01
    • 2019-03-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-12
    相关资源
    最近更新 更多