【问题标题】:CLISP - Reversing a simple listCLISP - 反转一个简单的列表
【发布时间】:2012-04-19 02:41:02
【问题描述】:

我必须反转一个简单(一维)列表的元素。我知道有一个内置的反向功能,但我不能用它来做这个。

这是我的尝试:

(defun LISTREVERSE (LISTR)
    (cond
        ((< (length LISTR) 2) LISTR) ; listr is 1 atom or smaller
        (t (cons (LISTREVERSE (cdr LISTR)) (car LISTR))) ; move first to the end
    )
)

输出非常接近,但是是错误的。

[88]> (LISTREVERSE '(0 1 2 3)) 
((((3) . 2) . 1) . 0)

所以我尝试使用append 而不是cons

(t (append (LISTREVERSE (cdr LISTR)) (car LISTR)))

但是得到了这个错误:

*** - APPEND: A proper list must not end with 2

有什么帮助吗?

【问题讨论】:

  • 使用 LENGTH 不是一个好主意。它破坏了链接 cons 单元列表的目的。 LENGTH 遍历整个列表以确定长度。
  • APPEND 错误有点神秘,但表示最后的参数不是列表,而是数字 2。
  • 除非我弄错了,你的append 版本应该可以工作,只要你将最后一个参数放入列表中。

标签: lisp common-lisp clisp


【解决方案1】:

我可以给你一些建议,因为这看起来像家庭作业:

  • 递归的基本情况是当列表为空 (null) 时,not 当列表中的元素少于两个时
  • 考虑定义一个带有额外参数的辅助函数,一个在空列表中初始化的“累加器”。对于原始列表中的每个元素,cons 它位于累加器的头部。当输入列表为空时,返回累加器

顺便说一句,上面的解决方案是尾递归的。

【讨论】:

  • 我试图用基本情况为空来做,但后来我得到了((((NIL 3) 2) 1) 0)。我也知道 lisp 需要尾递归才能快速运行,但是当规范说“函数”时我不想过度使用它。
  • @user1342836 你得到了那些奇怪的结果精确地,因为你没有使用参数作为累加器来保存你的结果。在这种情况下,最简单的解决方案也恰好是有效的解决方案:使用累加器 - 顺便说一下,它是尾递归,你不会“过度”它。
  • 我让它在没有尾递归的情况下工作,但只是因为它是一个单维列表(我可以看到需要更多维度)。不过,null 是基本情况。谢谢你的提示
  • @user1342836 太棒了!对不起,我不能给你一个直接的答案,做人家作业是违反网站政策的。无论如何,如果这个答案对您有帮助,请考虑接受它(单击左侧的复选标记)
【解决方案2】:

作为 Óscar López 的后续行动(并与写下不同解决方案的诱惑作斗争):

  • 同时使用appendlength 使得发布的解决方案几乎是最不有效的反转列表的方法。查看consnull 上的文档,了解如何实现此功能的一些更好的想法。
  • 请,indent properly
  • 在这种情况下,尾递归确实更有效且相当简单。如果您还没有尝试过。 labels 是您要用于定义本地递归函数的形式。
  • 可能值得您翻阅The Little Schemer。一般来说,它会让您对递归有更好的感觉。

【讨论】:

  • 那些缩进规则很愚蠢。为答案的其他部分 +1。
  • @SethCarnegie - 我推断你是一个 C++ 或 Java 程序员,其编辑器不进行paren-matching。
  • 该页面给出的缩进系统被绝大多数 Lisp 编程世界所遵循。我怀疑你看了第一个例子,并没有看到上面的句子“不要不要这样写:”。
  • Common Lisp 编译器不需要支持尾递归。这是 Lisp 中的优化,而不是 Scheme 中的语义要求。
【解决方案3】:

没关系,你做了什么。您只是错过了结果列表的组成。

想一想:你必须将 CAR 的 1 元素列表附加到反向 CDR 列表的末尾:

(defun LISTREVERSE (LISTR)
    (cons
        ((< (length LISTR) 2) LISTR) ; listr is 1 atom or smaller
        (t (append (LISTREVERSE (cdr LISTR)) (list (car  LISTR))))))

【讨论】:

    【解决方案4】:
    (defun listreverse (list)
      (let (result)
        (dolist (item list result)
          (push item result))))
    
    • 当有一种简单的迭代方法可以达到相同的目标时,不要在 Common Lisp 中使用递归。 Common Lisp 不对尾递归做出任何保证,并且您的尾递归函数调用可能不会由编译器自行优化为跳转。
    • push 将项目添加到结果中
    • dolist 有一个可选的第三个参数,它是返回的值。退出循环时对其进行评估。

    【讨论】:

      猜你喜欢
      • 2016-08-22
      • 2018-08-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-10-22
      • 1970-01-01
      • 1970-01-01
      • 2011-08-24
      相关资源
      最近更新 更多