Scheme 递归格式中的置换

Scheme 递归格式中的置换,scheme,Scheme,我发现下面这段代码在Scheme中进行了排列。我的意思是,如果我输入like arguments'(1 2 3),它会给我: ((1 2 3) (1 3 2) (2 1 3) (2 3 1) (3 1 2) (3 2 1)) 代码如下: (define (remove x lst) (cond ((null? lst) '()) ((= x (car lst))(remove x (cdr lst))) (else (cons (car lst) (remove x

我发现下面这段代码在Scheme中进行了排列。我的意思是,如果我输入like arguments'(1 2 3),它会给我:

((1 2 3) (1 3 2) (2 1 3) (2 3 1) (3 1 2) (3 2 1))
代码如下:

(define (remove x lst)
  (cond
    ((null? lst) '())
    ((= x (car lst))(remove x (cdr lst)))
    (else (cons (car lst) (remove x (cdr lst))))))

(define (permute lst)
  (cond
    ((= (length lst) 1)(list lst))
    (else (apply append(map(lambda (i) (map (lambda (j)(cons i j))
                                            (permute (remove i lst))))lst)))))
第一个函数remove似乎很简单,它仅通过将x表示的字符与列表的开头进行比较并递归调用其余部分,来除去x表示的字符,即使它是否重复

我不太明白的部分是排列函数。据我所知,map将一个函数应用于参数的每个元素(在本例中是一个列表),而apply只将一个函数一次完全应用于所有参数。那么,到底是什么在做这一行:

(apply append(map(lambda (i) (map (lambda (j)(cons i j))
                                                (permute (remove i lst))))lst)))))
对我来说,它似乎只是想创建一个包含两个元素的对:i和j,它们将成为一个元素排列的列表(如果我们假设一个列表只是一组串联的对)。但是,再次调用的部分与我置换和移除,那部分在做什么?它只是删除列表的头以生成列表的子集,该列表的头元素i是固定的,直到元素用完为止

有什么帮助吗


谢谢

我总是发现从更高的角度理解算法更容易 在深入实现并试图理解之前,请先将级别设置为 那里发生了什么事。所以问题是:什么是排列 一份清单,你如何找到它们

单元素列表的排列显然就是列表 本身

(ab)
的排列是集合
[(ab)(ba)]

(a b c)
的排列是集合

[(a b c)(a c b)(b c a)(b a c)(c a b b)(c b a)]

总的来说有n!长度为n的列表的置换-我们有n 第一个元素的选择,一旦我们选择了,(n-1)个选择 对于第二个元素,(n-2)对于第三个元素,依此类推。这 当我们固定越来越多的第一个自由度时,自由度会减少 列表中的元素很有启发性:也许我们可以代表 求长度为n的列表的置换 长度列表的排列(n-1),依此类推,直到到达 单个元素列表的排列

事实证明,列表a的排列正好是集合 [元素在列表\元素的排列前面,对于每个 元素在列表中]

查看
(a b c)
案例确认这是 正确-我们有
a
前面的
(bc)
(cb)
,它们是
(bc)
b
前面的
(ac)
(ca)
等的排列。这 将元素前置到子列表的操作可以定义为

(define (prepend j)
  (cons element j))
以及为所有人做这件事的操作 子列表的排列将是
(map prepend(排列
子列表))
。现在,为每个元素定义一个新的前置函数是 也许是杀伤力过大——尤其是因为它们都有相同的形状。那么 更好的方法是只使用lambda,它捕获 审议中的因素。所需的操作是 然后
(映射(lambda(j)(cons元素j))(排列子列表))
。现在,我们 要将此操作应用于列表的每个元素,以及 使用另一张地图进行此操作,给出:

(map (lambda (element)
       (lambda (j) (cons element j) (permute sublist)))
     list)
现在,这看起来不错,但有一个问题:递归的每个阶段都需要一个 元素并将其转换为列表。对于长度为1的列表,这很好, 但对于较长的列表,它会在每次递归调用中重复,我们得到 嵌套非常深的列表。我们真正想做的是 这些列表在相同的基础上,这正是
(apply append…
所关注的。而这几乎是所有的线。唯一的 缺少的是如何首先生成子列表。但是 这也很简单-我们将只使用
删除
,这样子列表=
(删除元素列表)
。把所有的东西放在一起,我们有

(apply append (map (lambda (i)
                      (lambda (j) (cons i j))
                      (permute (remove i lst)))
                    lst))

基本情况处理长度=1的情况,其他所有情况都可以从那里找到

我总是发现从更高的角度理解算法更容易 在深入实现并试图理解之前,请先将级别设置为 那里发生了什么事。所以问题是:什么是排列 一份清单,你如何找到它们

单元素列表的排列显然就是列表 本身

(ab)
的排列是集合
[(ab)(ba)]

(a b c)
的排列是集合

[(a b c)(a c b)(b c a)(b a c)(c a b b)(c b a)]

总的来说有n!长度为n的列表的置换-我们有n 第一个元素的选择,一旦我们选择了,(n-1)个选择 对于第二个元素,(n-2)对于第三个元素,依此类推。这 当我们固定越来越多的第一个自由度时,自由度会减少 列表中的元素很有启发性:也许我们可以代表 求长度为n的列表的置换 长度列表的排列(n-1),依此类推,直到到达 单个元素列表的排列

事实证明,列表a的排列正好是集合 [元素在列表\元素的排列前面,对于每个 元素在列表中]

查看
(a b c)
案例确认这是 正确-我们有
a
前面的
(bc)
(cb)
,它们是
(bc)
b
前面的
(ac)
(ca)
等的排列。这 将元素前置到子列表的操作可以定义为

(define (prepend j)
  (cons element j))
以及为所有人做这件事的操作 子列表的排列将是
(map prepend(排列
子列表))
。现在,为每个元素定义一个新的前置函数是 也许杀伤力太大了,尤其是在t
> (map (lambda (j) (cons i j)) (permute (remove i lst)))
((1 2 3) (1 3 2))
> (map (lambda (i) (map (lambda (j) (cons i j))
>                       (permute (remove i lst))))
>      lst)
(((1 2 3) (1 3 2)) ((2 1 3) (2 3 1)) ((3 1 2) (3 2 1)))
> (append '(1 2) '(3 4) '(5 6))
(1 2 3 4 5 6)
> (apply append '((1 2) (3 4) (5 6)))
(1 2 3 4 5 6)