在没有映射函数的列表中生成排列

Posted

技术标签:

【中文标题】在没有映射函数的列表中生成排列【英文标题】:Generating permutations in Lisp withous map functions 【发布时间】:2021-03-31 16:04:12 【问题描述】:

我想在 Lisp 中生成一组所有排列的列表。这是我尝试过的:

(defun ins(e n l)
    (cond
        ((equal n 1) (cons e l))
        (T (cons (car l) (ins e (1- n) (cdr l))))
    )
)

;; (print (ins '1 1 '(2 3)))
;; (print (ins '1 2 '(2 3)))
;; (print (ins '1 3 '(2 3)))

(defun insert(e n l)
    (cond
        ((equal n 0) nil)
        (T (cons (ins e n l) (insert e (1- n) l) ))
    )
)

;; (print (insert '1 3 '(2 3)))

(defun inserare(e l)
    (insert e (1+ (length l)) l)
)

;(print (inserare '1 '(2 3)))  -> ((2 3 1) (2 1 3) (1 2 3)) 

从这里我无法实现最终的排列函数。我尝试过这样的事情:

(defun perm(L)
    (cond
        ((null L) nil)
        (T (append (perm (cdr L)) (inserare (car L) L)))  
    )
)

但这不是好方法

【问题讨论】:

查看sourceforge.net/p/clocc/hg/ci/default/tree/src/cllib/…附近的代码 我明白了,但我想以纯递归方式执行此操作,不使用循环。而且这看起来也是一个非常专业的 Lisp 代码,对我来说很难 【参考方案1】:

这是一种方法。

首先,如果你有一个像(x . y) 这样的列表并且你有y 的排列,你需要从它们中创建(x . y) 的排列。考虑一下这些排列中的一个p,并让它成为(p1 p2 ...)。从这里你需要做一堆排列,包括x: (x p1 p2 ...), (p1 x p2 ...), (p1 p2 x ...) ... (p1 p2 ... x)

所以让我们编写一个函数来执行此操作:一个给定某个对象和一个列表的函数将通过列表“线程化”该对象,创建一堆新列表,并在每个点插入对象。由于将变得很清楚的原因,此函数将采用一个额外的参数,该参数是将新排列附加到前面的事物列表。它还将使用一些本地函数来完成真正的工作。

这里是:

(defun thread-element-through-list (e l onto)
  (labels ((tetl-loop (erofeb after into)
             (if (null after)
                 (cons (nreverse (cons e erofeb)) into)
               (tetl-loop (cons (first after) erofeb)
                          (rest after)
                          (cons (revappend (cons e erofeb) after) into)))))
    (tetl-loop '() l onto)))

我不打算解释这个函数的细节,但有几点值得了解:

tetl-loopthread-element-through-list-loop; erofebbefore 倒过来的,因为上面的元素是倒序排列的; nreverse 只是一个免费的 hack,因为此时 erofeb 已经死了 - 这个函数实际上没有任何突变。

我们可以测试一下:

> (thread-element-through-list 1 '(2 3) '())
((2 3 1) (2 1 3) (1 2 3))

现在,好的,我们实际上拥有的不仅仅是y一个 排列,我们还有它们的列表。我们需要使用 `thread-element-through-list.所以我们需要一个函数来做到这一点。

(defun thread-element-through-lists (e ls onto)
  (if (null ls)
      onto
    (thread-element-through-lists
     e (rest ls)
     (thread-element-through-list e (first ls) onto))))

这还有一个参数,它定义了将结果添加到什么,你可以看到这个onto 列表现在是如何被传递来构建列表的。

我们可以测试一下

> (thread-element-through-lists '1 '((2 3) (3 2)) '())
((3 2 1) (3 1 2) (1 3 2) (2 3 1) (2 1 3) (1 2 3))

仔细看。我给了thread-element-through-lists1 和一个列表,它是(2 3) 的排列,而我发现了(1 2 3) 的排列。所以你现在需要做的(我不会为你做的)是编写一个函数:

知道()(即())和单元素列表(即包含该列表的列表)的排列; 使用thread-elements-through-lists 和对自身的递归调用来计算任何更长列表的排列。

【讨论】:

或者我们可以 turn it inside out 和 uncons 很多,而不是 consing。 :) 每个排列将依次单独提供;我们可以将它们复制到结果列表中,或者只是一个一个地处理它们。 @WillNess:是的,这就是我在现实生活中会做的事情,或者类似的事情。

以上是关于在没有映射函数的列表中生成排列的主要内容,如果未能解决你的问题,请参考以下文章

如何通过pl sql函数从表中生成列表?

如何从所有排列中生成所有可能的组合?

循环,从数组中生成带有函数的按钮

如何在访问报告中生成列表?

没有部分的python lambda函数列表

在 Clang 中生成默认构造函数