【问题标题】:New to lisp, compiler telling me this is a bad lambdalisp 新手,编译器告诉我这是一个糟糕的 lambda
【发布时间】:2015-09-30 02:24:45
【问题描述】:

我是 lisp 的新手,在我大学生涯的这个阶段,我对 lambda 表达式的经验很少。这是一个家庭作业练习,问题是:

  1. 编写一个 LISP 函数 COUNTALLNUMBERS 来计算列表中数字原子的数量,无论它们嵌套多深。例如: (COUNTALLNUMBERS ‘(1 A 2 (3 B 4 (5 6)))) 返回值 6

我也在使用 Lispworks 来编译我的代码并且一直收到错误:

 Badly formed lambda: (NUMBERP (CAR LIST) (+ 1 (COUNTALLNUMBERS (CDR LIST))))

这是我的代码:

(defun countallnumbers(list)
  (if (eql list nil)
      nil 
    (let ((elem (car list))
          (restlist (cdr list)))
      (if (listp elem)
          (append (countallnumbers elem) (countallnumbers restlist))
        (append (cons elem nil) (countallnumbers restlist)))
      ((numberp (car list) (+ 1 (countallnumbers (cdr list))))))))

(write (countallnumbers '(1 2 3 a b (4 c))))

到目前为止,我已经进行了测试,并且一切正常,以调用 numberp 开始。我认为如果将输入转换为仅包含原子的列表,而不是嵌套,那么处理所有内容将是最容易的。任何有助于理解这一点的帮助将不胜感激

【问题讨论】:

  • 请正确格式化您的代码。就像现在呈现的那样,它是不可读的。
  • 我可以做些什么来改进格式?我在课堂上看到的最 lisp 是非常短的函数。
  • 一个风格指南在 Practical Common Lisp 中(顺便说一句很棒的书):gigamonkeys.com/book/…
  • 使用Emacs 缩进。
  • @coredump: flatten 创建一个新列表;数数不一定要。

标签: lambda lisp common-lisp


【解决方案1】:

语法错误在:

((numberp (car list) (+ 1 (countallnumbers(cdr list)))))

因为,给定你的程序,这个列表被读取为一个表单,它的第一个元素应该是一个符号函数或一个 lambda 列表(即(lambda (args) body)),其余的是这个函数的参数。另一方面,如果您将其写为要评估的条件(给定谓词numberp),那么它会写在只有一个条件的if 形式之外(与cond 不同)。

您的函数有一个主要问题:它应该返回一个数字,但是当输入为空列表(结果应该是0,而不是nil)以及通过@987654328 时,您都会返回列表@ 在其他情况下。

还有其他一些小的风格问题:

  1. 测试空列表的常用方法是使用(endp list),而不是(eql list nil)
  2. 右括号不应单独写成一行。

这是函数的可能定义:

(defun countallnumbers(list)
  (cond ((endp list) 0)
        ((numberp (car list)) (1+ (countallnumbers (cdr list))))
        ((listp (car list)) (+ (countallnumbers (car list)) (countallnumbers (cdr list))))
        (t (countallnumbers (cdr list)))))

请注意,cond 用于代替if,因为必须检查四种情况:当列表为空时,当汽车是数字时,或者它是列表,或者既不是数字也不是列表.在所有情况下,都会返回一个整数,包括终止情况、列表为空时以及递归情况。 1+ 是对其参数加 1 的函数,而请注意,在 car 是一个列表的情况下,结果是通过将汽车的数量与汽车的数量相加得到的列表的 cdr。

【讨论】:

  • 测试空正确列表的常用方法是endp
  • 谢谢,@Svante,我太老了,经常忘记这一点。我会改变答案。
  • 好的,我知道你对这个条件做了什么。我正在逐个构建该功能,并在最初使用 if ,但将来会使用条件代替多个 if。以后我也会用endp。其余的现在是有道理的,谢谢。 @Svante
【解决方案2】:
  1. 照你老师说的做,但要注意教科书要你写丑陋的函数名。我会在这里写count-all-numbers(人们很少写“LISP”,而是“Lisp”)。

  2. 列表的求值对第一个元素的处理方式与其他元素不同。它应该是一个特殊的运算符或函数指示符:在当前环境中是 fboundp 的符号或文字 (lambda ...) 形式。 (numberp ...) 两者都不是,它是一个常规列表。这就解释了为什么会出现错误。

  3. 使用工具来格式化您的代码(emacs/paredit)。如果您没有,您仍然可以使用 pprint 并引用您的函数来查看表达式通常是如何格式化的(仍然有机会以更人性化的方式布局代码)。 结果很可能是大写的。我一般将*print-circle*绑定到:downcase,像这样:

    (let ((*print-circle* :downcase))
      (pprint '(defun ...)))
    
  4. 最简单的递归解决方案是:

    (defun count-all-numbers (form)
       (typecase form
         (cons (+ (count-all-numbers (car form))
                  (count-all-numbers (cdr form))))
         (number 1)
         (t 0)))
    

    可能有更有效的方法来做到这一点,但您必须在这开始成为问题之前处理非常大的列表。

【讨论】:

    猜你喜欢
    • 2011-01-13
    • 1970-01-01
    • 1970-01-01
    • 2014-09-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-07
    • 1970-01-01
    相关资源
    最近更新 更多