简体   繁体   English

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

[英]Build list in Lisp using an iterative recursive function

Super newbie in Lisp but at least I am trying so forgive me if the approach seems a bit strange. Lisp 的超级新手,但至少我正在努力,如果这种方法看起来有点奇怪,请原谅我。
I am open to learn new ideas but I will definitely need to learn whats wrong with my approach.我愿意学习新想法,但我肯定需要了解我的方法有什么问题。
I have a function that builds a list using an iterative recursive approach.我有一个使用迭代递归方法构建列表的函数。

(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))
        )
    )
)

Input to my program is我的程序的输入是

'(1 2 3)

Expected output is预期输出是

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

My loops (recursion) runs file.我的循环(递归)运行文件。 I have issues in combing the output of recursion appropriately.我在适当地组合递归输出时遇到问题。

Code style代码风格

  • It is preferable to not have closing parentheses on their own lines, the usual convention in Lisp is to have parentheses grouped at the end, like so: ))) .最好不要在自己的行上使用右括号,Lisp 中通常的约定是将括号放在最后,如下所示:) )))

  • Since cons-cells have only two slots, CAR and CDR , when they are used as a list they do not hold the length of the list.由于 cons-cells 只有两个时隙, CARCDR ,当它们用作列表时,它们不保存列表的长度。 So the only way to compute the length is to traverse the whole chain of cells, which is exactly what your function is already doing.所以计算长度的唯一方法是遍历整个单元链,这正是你的函数已经在做的。 If your list is of size N, you'll have to compute the length N times, which makes the number of steps in your function proportional to N*N.如果列表的大小为 N,则必须计算长度 N 次,这使得函数中的步数与 N*N 成正比。

  • In a loop, a single DO can be followed by multiple expressions, you do not need to repeat the DO keyword.在一个循环中,一个 DO 后面可以跟多个表达式,不需要重复 DO 关键字。 Also, add spaces before opening parentheses.另外,在开括号之前添加空格。

  • SETQ is not supposed to be applied with unbound variables, like subtree or sub-sub-list . SETQ 不应应用于未绑定的变量,例如subtreesub-sub-list You should first have a surrounding let where you introduce local variables (eg around the loop ), or use with clauses in your loop.你应该首先有一个周围的let引入局部变量(例如围绕loop ),或者在你的循环中使用with子句。 Or better, use the existing facilities of LOOP to avoid doing the mutation yourself.或者更好的是,使用 LOOP 的现有设施来避免自己进行变异。

  • The return value of APPEND is important, since it 's the results of appending the arguments (which are left unmodified). APPEND的返回值很重要,因为它是附加参数(未修改)的结果。 But here you do not use the return value, which makes the whole expression useless.但是这里你没有使用返回值,这使得整个表达式毫无用处。

Alternative选择

Instead of computing the length, it is sufficient to check whether the input lists is empty, contains one elements or more (without counting).无需计算长度,只需检查输入列表是否为空、是否包含一个或多个元素(不计算)就足够了。 Also, you can use collect to collect all trees as a list.此外,您可以使用collect将所有树收集为列表。 I am not sure the result for a singleton input list is correct, maybe it should be (list list) .我不确定单例输入列表的结果是否正确,也许应该是(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))))))

Some initial notes.一些初步的笔记。

  • When asking a question which involves implementing an algorithm describe the algorithm : it is not easy to guess what you want based on a single example (below I have made two guesses).当提出一个涉及实现算法的问题时,请描述算法:根据单个示例(下面我做了两个猜测)来猜测您想要什么并不容易。
  • I would guess you have written in Python previously, as your code shows significant signs of 'Python braindamage' (note this is a comment about Python, which I've spent years of my life on, not about your ability).我猜你以前是用 Python 编写的,因为你的代码显示出明显的“Python 脑损伤”迹象(请注意,这是对 Python 的评论,我花了很多年的时间,而不是关于你的能力)。 In particular:特别是:
    • Lisp does not confuse forms which create new bindings (variables) with assignment the way Python does. Lisp 不会像 Python 那样将创建新绑定(变量)的表单与赋值混淆。 You don't create a new binding in a function by setq you create it by some binding form such as let ;您不会通过setq在函数中创建新绑定,而是通过某种绑定形式(例如let创建它;
    • You don't add new things to the end of a list by append , you create a new list which has the new things added to it, and since lists are linked lists and not variable-length arrays in drag, append takes time proportional to the length of the list.您不会通过append在列表末尾添加新内容,而是创建一个新列表,其中添加了新内容,并且由于列表是链接列表而不是拖动中的可变长度数组,因此append花费的时间与列表的长度。
  • But in one respect your code ignores an important lesson of Python: all those group-closing markers don't matter to anyone reading the code, and you should not fill lines with single group closing (or opening) markers.但在一方面,您的代码忽略了 Python 的一个重要教训:所有这些组结束标记对阅读代码的任何人都无关紧要,并且您不应该使用单个组结束(或开始)标记填充行。 They are just noise which makes reading code hard.它们只是使阅读代码变得困难的噪音。 In Python, in fact, they are so invisible they don't exist at all.事实上,在 Python 中,它们是如此隐形,以至于根本不存在。 This is one of the things Python got right.这是 Python 做对的事情之一。

That being said, here are three versions of what I think you want: the first implements what I'd think of as a consistent algorithm, the second implements what I think you may want, and the final one abstracts out the termination test & can do either (or anything else)).话虽如此,这里有我认为你想要的三个版本:第一个实现我认为的一致算法,第二个实现我认为你可能想要的,最后一个抽象出终止测试 & 可以做任何一个(或其他任何事情))。

(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)))

As examples of these:作为这些例子:

> (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)))

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM