在Lisp中使用迭代递归函数构建列表

时间:2019-02-27 18:35:13

标签: list lisp common-lisp

Lisp中的超级新手,但至少我尝试如此原谅我,如果方法看起来有些奇怪。
我乐于学习新想法,但我绝对需要学习我的方法有什么问题。
我有一个使用迭代递归方法构建列表的函数。

(defun create-tree-iteratively(sub-list)
    (if (equal (length sub-list) 1)
        sub-list
        (loop for i in sub-list
            do(setq subtree (list i))
            do(setq sub-sub-list (remove i sub-list))
            do(append subtree (create-tree-iteratively sub-sub-list))
        )
    )
)

我的程序的输入是

'(1 2 3)

预期输出为

'((1 (2 3) (3 2)) (2 (1 3) (3 1)) (3 (1 2) (2 1)))

我的循环(递归)运行文件。我在适当地组合递归的输出时遇到问题。

2 个答案:

答案 0 :(得分:4)

代码样式

  • 通常情况下,最好不要在自己的行上加上右括号 Lisp中的约定是在括号的末尾进行分组,如下所示: )))

  • 由于约束单元只有两个插槽CARCDR,因此当它们用作列表时,它们不包含 列表的长度。所以唯一的方法 计算长度是要遍历整个单元链,这正是 您的功能已经在做什么。如果您的清单大小为N,则必须 计算长度N次,这会使函数中的步数与N * N成比例。

  • 在一个循环中,一个DO可以跟多个表达式,您不需要 重复DO关键字。另外,在括号前添加空格。

  • SETQ不应与未绑定变量一起使用,例如subtreesub-sub-list。首先,您应该在周围let进行介绍 局部变量(例如,loop附近),或在您的变量中使用with子句 环。或者更好的方法是,使用现有的LOOP工具避免进行突变 你自己。

  • APPEND的返回值很重要,因为它是 附加参数的结果(保留不变)。但是你在这里 不使用返回值,这会使整个表达式无用。

替代

检查输入是否足够,而不是计算长度 列表为空,包含一个或多个元素(不计)。另外,你可以 使用collect收集所有树木作为列表。我不确定结果是否 单例输入列表正确,也许应该是(list list)

(defun create-tree (list)
  (if (null (rest list))
      ;; covers both empty list and list with a single element
      list
      ;; otherwise, collect a list of trees
      (loop
        for i in list
        ;; collect all trees rooted at i, where a tree is a list (r c1 .. cn)
        ;; with R the root node and C1...CN each child tree. The child trees
        ;; are build recursively, with i removed from the list of values.
        collect (list* i (create-tree (remove i list))))))

答案 1 :(得分:4)

一些初始笔记。

  • 在问一个涉及实现算法的问题时,描述算法:根据一个例子来猜测您想要的东西并不容易(下面我已经做了两次猜测)。
  • 我猜您以前用Python编写过代码,因为您的代码显示出“ Python脑损伤”的明显迹象(请注意,这是关于Python的评论,我花了我很多年的时间,而不是您的能力)。尤其是:
    • Lisp不会混淆创建新绑定(变量)的表单以及Python分配方式。您不会通过setq在函数中创建新绑定,而不会通过诸如let;
    • 之类的绑定形式来创建新绑定
    • 您不会通过append在列表末尾添加新内容,而是创建一个添加了新内容的新列表,并且由于列表是链接列表,而不是列表中的可变长度数组拖动append所花费的时间与列表的长度成正比。
  • 但是在一个方面,您的代码忽略了Python的重要一课:所有那些关闭组的标记对于阅读代码的任何人都没有关系,并且您不应该使用单个关闭或打开组的标记来填充行。它们只是噪音,使得难以阅读代码。实际上,在Python中,它们是如此隐蔽,根本不存在。这是Python正确的事情之一。

话虽如此,这是我想要的三个版本:第一个实现了我认为是一致算法的东西,第二个实现了我认为你想要的东西,以及最后一个抽象出终止测试,并且可以(或做其他任何事情)。

(defun make-permuted-tree (l)
  ;; this builds the tree all the way down
  (if (null l)
      '()
    (loop for e in l
          collect (cons e (make-permuted-tree (remove e l))))))

(defun make-permuted-tree/strange (l)
  ;; this stops before the end
  (if (null (rest l))
      l
    (loop for e in l
          collect (cons e (make-permuted-tree/strange (remove e l))))))

(defun make-permuted-tree/general (l &key (base-test (lambda (b)
                                                       (null b))))
  ;; this stops where you want it to, which by default is at the end
  (labels ((make-permuted-tree (lt)
             (if (funcall base-test lt)
                 lt
               (loop for e in lt
                     collect (cons e (make-permuted-tree (remove e lt)))))))
    (make-permuted-tree l)))

例如:

> (make-permuted-tree/strange '(1 2 3))
((1 (2 3) (3 2)) (2 (1 3) (3 1)) (3 (1 2) (2 1)))

> (make-permuted-tree '(1 2 3))
((1 (2 (3)) (3 (2))) (2 (1 (3)) (3 (1))) (3 (1 (2)) (2 (1))))

> (make-permuted-tree/general '(1 2 3))
((1 (2 (3)) (3 (2))) (2 (1 (3)) (3 (1))) (3 (1 (2)) (2 (1))))

> (make-permuted-tree/general '(1 2 3) :base-test (lambda (b)
                                                    (null (rest b))))
((1 (2 3) (3 2)) (2 (1 3) (3 1)) (3 (1 2) (2 1)))