【问题标题】:Explication of Scheme code for permutations置换方案代码的解释
【发布时间】:2020-09-05 06:37:55
【问题描述】:

这是一个生成元素列表排列的 Scheme 代码:

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

如果我们将代码拆开,我确实了解代码的每个部分,但我无法理解的是这一切如何导致生成排列?

假设我们取列表‘(a b),它是如何生成‘(a b)‘(b a)的?

我们首先从列表中删除a,然后b 保留,但是在哪里写到你现在必须将a 改为bb 是单个元素,但在我对代码的解释中,b 也将被删除,什么都没有了……

【问题讨论】:

  • 理解代码的第一步是将其格式化以供人类使用。把它分成几行,适当地缩进。你将无法阅读这个矩形的东西,我们也不会。有几个编辑器对方案(vim,emacs,...)具有完全合理的自动缩进。
  • @dratenik 这只是部分他们的错,但主要是SO formatting fault。在小修复之后,剩下的有点特殊,是的,但格式仍然很可读。
  • see this 以获得相关见解。 (apply append (map f xs)) == (flatmap f xs).
  • @alinsoar OP 已经提供了一个定义,他们一般不会问如何实现这个。它在列表中,而在例如数组它可以完全不同。所以我不明白你删除这两个标签。
  • @alinsoar 这个特定问题是关于列表排列的给定定义。我正在恢复标签,我觉得它们非常相关。 .... 好的,我取出了列表标签。 :)

标签: scheme permutation definition


【解决方案1】:

我会像这样阅读主要部分(按数字表示的顺序)

   (map (lambda (i)     ;(1) for each i in...
      (map (lambda (j)  ;(3) for each j in...
           (cons i j))  ;(6) append i to front (of this shorter permutation)
         (permute       ;(4) ...the list of all permutations...
           (remove i lst))))    ;(5) ...of the input list with i removed
    lst)                ;(2) ... the input list

【讨论】:

    【解决方案2】:

    TL;DR:口头解释在这个答案的最后。)

    让我们尝试遵循let*-rewrites 的定义。定义是

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

    我们尝试

    (permute '(a b))
    ≡
    (let* ((lst '(a b)))
      (apply append
         (map (lambda (i)
                 (map (lambda (j) (cons i j))
                      (permute (remove i lst))))
              lst)))
    ≡
    (let* ((lst '(a b))
           (r  (map (lambda (i)
                      (map (lambda (j) (cons i j))
                           (permute (remove i lst))))
                    lst)))
      (apply append r))
    ≡
    (let* ((lst '(a b))
           (i1 'a)
           (r1 (map (lambda (j) (cons i1 j))
                           (permute (remove i1 lst))))
           (i2 'b)
           (r2 (map (lambda (j) (cons i2 j))
                           (permute (remove i2 lst))))
           (r (list r1 r2)))
      (apply append r))
    ≡
    (let* ((lst '(a b))
           (i1 'a)
           (t1 (permute (remove i1 lst)))
           (r1 (map (lambda (j) (cons i1 j)) t1))
           (i2 'b)
           (t2 (permute (remove i2 lst)))
           (r2 (map (lambda (j) (cons i2 j)) t2))
           (r (list r1 r2)))
      (apply append r))
    ≡
    (let* ((i1 'a)
           (t1 (permute '(b)))
           (r1 (map (lambda (j) (cons i1 j)) t1))
           (i2 'b)
           (t2 (permute '(a)))
           (r2 (map (lambda (j) (cons i2 j)) t2))
           (r (list r1 r2)))
      (apply append r))
    ≡
    (let* ((i1 'a)
           (t1 '( (b) ))
           (r1 (map (lambda (j) (cons i1 j)) t1))
           (i2 'b)
           (t2 '( (a) ))
           (r2 (map (lambda (j) (cons i2 j)) t2))
           (r (list r1 r2)))
      (apply append r))

    所以我们得到

    (let* ((r1 (map (lambda (j) (cons 'a j)) '( (b) )))
           (r2 (map (lambda (j) (cons 'b j)) '( (a) )))
           (r (list r1 r2)))
      (apply append r))
    ≡
    (let* ((r1 (list (cons 'a '(b))))
           (r2 (list (cons 'b '(a))))
           (r (list r1 r2)))
      (apply append r))
    ≡
    (let* ((r1 (list '(a b)))
           (r2 (list '(b a)))
           (r (list r1 r2)))
      (apply append r))
    ≡
    (let* ((r1  '((a b)))
           (r2  '((b a)))
           (r (list r1 r2)))
      (apply append r))
    ≡
    (apply append (list '((a b)) '((b a))))
    ≡
    (      append       '((a b)) '((b a)) )
    ≡
    '(                    (a b)    (b a)  )

    如果您需要让自己相信中间结果的有效性,请遵循相同的技巧。


    事后看来,我们可以更积极地简化它,比如

    (let* ((lst '(a b))
           (i1 'a)
           (r1 (map (lambda (j) (cons i1 j))
                           (permute (remove i1 lst))))
           (i2 'b)
           (r2 (map (lambda (j) (cons i2 j))
                           (permute (remove i2 lst))))
           (r (list r1 r2)))
      (apply append r))
    ≡
    (let* ((lst '(a b))
           (i1 'a)
           (t1 (permute (remove i1 lst)))
           (r1 (map (lambda (j) (cons i1 j)) t1))
           (i2 'b)
           (t2 (permute (remove i2 lst)))
           (r2 (map (lambda (j) (cons i2 j)) t2)))
      (apply append (list r1 r2)))
    ≡
    (let* ((t1 (permute '(b)))
           (r1 (map (lambda (j) (cons 'a j)) t1))
           (t2 (permute '(a)))
           (r2 (map (lambda (j) (cons 'b j)) t2)))
      (append r1 r2))
    ≡
    (let* ((r1 (map (lambda (j) (cons 'a j)) '( (b) )))
           (r2 (map (lambda (j) (cons 'b j)) '( (a) )))
           )
      (append r1        ; one row for each elt            '( a
              r2        ;  of the input list,                b
              ))        ;  spliced in place by append        )
    

    等等,最后以更直观的方式揭示了计算的结构:

    • 对于输入列表的每个元素,
      • 找出余数的所有排列,
      • 将该元素添加到每个元素,
    • 并通过将所有这些结果附加在一起,将处理输入列表中每个元素的结果连接在一起。

    (从而证明我的另一个基于伪代码的answer 在这里)。

    【讨论】:

    • this 答案末尾有口头解释。如果仍有不清楚的地方,请毫不犹豫地提出更多问题。 :)
    【解决方案3】:

    (apply append (map f xs)) == (flatmap f xs).

    用等式模式匹配伪代码重写您的代码,

    remove x [x, ...ys] = remove x ys           ; skip this x, and go on removing
                ; ( consider skipping just this one occurrence instead:
                ;       = ys                                           )
    remove x [y, ...ys] = [y, ...remove x ys]   ;   (or else x /= y, so keep this y)
    remove x [] = []                            ; until the list is exhausted
    
    permute [x] = [[x]]
    permute xs  = 
        xs                                ; ( with (xs |> f) == (f xs) )
        |> flatmap (x =>                  ; for each x in xs, 
              permute (remove x xs)       ;   for each permutation p of xs w/o x,
              |> map (p => [x, ...p]) )   ;      prepend x to p and 
                                          ;   splice the results in place of x
    

    这更清楚了吗?

    没有?那么,让我们看看permute [a,b]是如何计算出来的。

    首先,permute [a] 是什么?

      permute [a] = ...
    ( permute [x] = [[x]] )
              ... = [[a]]
    

    (我们如何称呼单元素列表的第一个元素并不重要,它仍然是它的第一个也是唯一的元素)。同样,

      permute [b] = ...
    ( permute [x] = [[x]] )
              ... = [[b]]
    

    好的,但它如何帮助我们查看permute [a,b] 的结果?让我们一步一步地使用它:

    permute [      a,          b       ] =
       ;; for each x in (xs==[a,b])
       ;;          a           b           ; <<- the value of x
       ;;      remove x from xs
       ;;         [b]         [a]          ; <<- xs with x removed
       ;;      prepend x to each permutation of the above
       ;;       [[  b]]     [[  a]]        ; <<- permutations
       ;;       [[a,b]]     [[b,a]]        ; <<- prefixed with x
       ;;      splice them in by `apply append`
            [    [a,b]   ,   [b,a]     ]
    

    那么,permute [b,c] == [[b,c],[c,b]] 等等。并且,有了这些知识,

    permute [        a,                b,                 c          ] =
       ;; for each x in (xs==[a,b,c])
       ;;      remove x from xs
       ;;          [b,c]              [a,c]              [a,b]
       ;;      prepend x to each permutation of the above
       ;;    [[  b,c],[  c,b]]  [[  a,c],[  c,a]]  [[  a,b],[  b,a]]
       ;;    [[a,b,c],[a,c,b]]  [[b,a,c],[b,c,a]]  [[c,a,b],[c,b,a]]
       ;;      splice them in by `apply append`
            [ [a,b,c],[a,c,b],   [b,a,c],[b,c,a],   [c,a,b],[c,b,a]  ]
    

    这样更清楚吗?

    【讨论】:

    • 对不起,我完全是编程初学者。我想要的只是对我的问题的解释,所以:它是如何生成'(a b) 和'(b a) 的?我们首先从列表中删除 a 并且 b 保留,但是在哪里写到您现在必须将 a 与 b 相结合? b 是单个元素,但是在我对代码的解释中,b 也将被删除,并且什么都没有了……请只用文字而不是伪代码,谢谢。
    • "你现在必须从 a 到 b 的地方写在哪里?"在map 调用中,(map (lambda (j) (cons i j)).这里i'aj'(b)。后者之所以如此,是因为cond((= (length lst) 1) (list lst)) 中的第一个子句。您需要了解map,并且您需要了解递归,以了解您帖子中的这段代码。你确实说过你分别理解每一段代码。 :)
    • 感谢您的时间和精力。这不是更清楚,但我想问题出在我身上,我也害怕你无法向我解释这个练习,因为我要求用简单的语言来解释。仍然非常感谢您,祝您有愉快的一天!
    • @JeckCoeler 可能会从询问有关特定代码片段的更小、更集中的问题开始。你明白map 在做什么吗?你明白apply append 在做什么吗?具体是什么不清楚?例如,你明白(remove 'a '(a b)) == '(b),为什么?那(remove 'b '(a b)) == '(a),又如何? (permute '(b)) == '((b))?那(map (lambda (j) (cons 'a j)) (list '(b)) == (list (cons 'a '(b))) == '((a b))?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-07
    • 2012-04-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多