【问题标题】:Circular permutation in scheme方案中的循环置换
【发布时间】:2016-08-14 08:26:45
【问题描述】:

您好,我尝试使用递归在 Scheme (Dr. Racket) 中进行循环排列。

例如,如果我们有 (1 2 3),则循环置换给出 ((1 2 3) (2 3 1) (3 1 2))。

我写了一段代码,但我有一个问题要进行转变。

我的代码:

(define cpermit
  (lambda (lst)
    (cpermitAux lst (length lst))))

(define cpermitAux
  (lambda (lst n)
    (if (zero? n) '()
        (append (cpermitAux lst (- n 1)) (cons lst '())))))

其中 (cpermit '(1 2 3)) 给出 '((1 2 3) (1 2 3) (1 2 3))

【问题讨论】:

  • 这里的问题是您实际上从未对lst 进行任何会重新排列其元素的操作。它实际使用的唯一位置(而不是仅仅通过调用cpermitAux 将其线程化)在(cons lst '()) 中,它只使用lst 而没有修改。递归调用也只是传递 lst 而不做任何修改,所以你所要做的就是实现一个 replicate 函数。
  • 谢谢!我添加了一个旋转功能,现在它可以工作了。

标签: algorithm recursion scheme racket circular-permutations


【解决方案1】:

您可以使用移动列表的功能

(defun lshift (l) (append (cdr l) (list (car l))))

这会将您的列表向左移动。

在追加之前使用这个函数

(define cpermit
  (lambda (lst)
    (cpermitAux lst (length lst))))

(define cpermitAux
  (lambda (lst n)
    (if (zero? n) '()
      (append (cpermitAux (lshift lst) (- n 1)) (lshift (cons lst '()))))))

【讨论】:

    【解决方案2】:

    这个答案是@rnso 的代码的一系列翻译,修改为使用递归辅助函数而不是重复的set!

    #lang racket
    (define (cpermit sl)
      ;; n starts at (length sl) and goes towards zero
      ;; sl starts at sl
      ;; outl starts at '()
      (define (loop n sl outl)
        (cond [(zero? n) outl]
              [else
               (loop (sub1 n) ; the new n
                     (append (rest sl) (list (first sl))) ; the new sl
                     (cons sl outl))])) ; the new outl
      (loop (length sl) sl '()))
    
    > (cpermit (list 1 2 3 4))
    (list (list 4 1 2 3) (list 3 4 1 2) (list 2 3 4 1) (list 1 2 3 4))
    

    对于这种递归帮助器的简写,您可以使用命名为let。这会将初始值放在顶部,以便于理解。

    #lang racket
    (define (cpermit sl)
      (let loop ([n (length sl)] ; goes towards zero
                 [sl sl]
                 [outl '()])
        (cond [(zero? n) outl]
              [else
               (loop (sub1 n) ; the new n
                     (append (rest sl) (list (first sl))) ; the new sl
                     (cons sl outl))]))) ; the new outl
    
    > (cpermit (list 1 2 3 4))
    (list (list 4 1 2 3) (list 3 4 1 2) (list 2 3 4 1) (list 1 2 3 4))
    

    对于@rnso,您可以认为nsloutl 与“可变变量”具有相同的目的,但这实际上与我之前定义@ 时编写的代码相同987654328@ 作为递归辅助函数。

    上述模式对于 Scheme/Racket 代码中的累加器非常常见。每次你想要这样的循环时,(cond [(zero? n) ....] [else (loop (sub1 n) ....)]) 写出来有点烦人。因此,您可以将for/fold 与两个累加器一起使用。

    #lang racket
    (define (cpermit sl)
      (define-values [_ outl]
        (for/fold ([sl sl] [outl '()])
                  ([i (length sl)])
          (values (append (rest sl) (list (first sl))) ; the new sl
                  (cons sl outl)))) ; the new outl
      outl)
    
    > (cpermit (list 1 2 3 4))
    (list (list 4 1 2 3) (list 3 4 1 2) (list 2 3 4 1) (list 1 2 3 4))
    

    您可能已经注意到,外部列表的最后一个是(list 1 2 3 4),倒数第二个是(list 2 3 4 1),等等。这是因为我们通过在前面添加@ 来从后到前构建列表987654334@。为了解决这个问题,我们可以在最后反转它。

    #lang racket
    (define (cpermit sl)
      (define-values [_ outl]
        (for/fold ([sl sl] [outl '()])
                  ([i (length sl)])
          (values (append (rest sl) (list (first sl))) ; the new sl
                  (cons sl outl)))) ; the new outl
      (reverse outl))
    
    > (cpermit (list 1 2 3 4))
    (list (list 1 2 3 4) (list 2 3 4 1) (list 3 4 1 2) (list 4 1 2 3))
    

    最后,(append (rest sl) (list (first sl))) 应该是它自己的辅助函数,因为它有一个明确的目的:旋转列表一圈。

    #lang racket
    ;; rotate-once : (Listof A) -> (Listof A)
    ;; rotates a list once around, sending the first element to the back
    (define (rotate-once lst)
      (append (rest lst) (list (first lst))))
    
    (define (cpermit sl)
      (define-values [_ outl]
        (for/fold ([sl sl] [outl '()])
                  ([i (length sl)])
          (values (rotate-once sl) ; the new sl
                  (cons sl outl)))) ; the new outl
      (reverse outl))
    
    > (cpermit (list 1 2 3 4))
    (list (list 1 2 3 4) (list 2 3 4 1) (list 3 4 1 2) (list 4 1 2 3))
    

    【讨论】:

      【解决方案3】:

      以下代码也有效(没有任何辅助函数):

      (define (cpermit sl)
        (define outl '())
        (for((i (length sl)))
          (set! sl (append (rest sl) (list (first sl))) )
          (set! outl (cons sl outl)))
        outl)
      
      (cpermit '(1 2 3 4))
      

      输出是:

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

      【讨论】:

      • 这很好但是,“没有任何辅助函数”不应该是一个目标。辅助函数是一件好事,特别是如果它们有明确的目的
      • 另外,你应该避免set!,因为命令式代码可以禁用优化。相反,您可以使用递归辅助函数。如果需要,您可以在 cpermit 函数的主体中定义辅助函数,我会发布一个将您的答案翻译成该模式的答案
      【解决方案4】:

      以下解决方案功能强大且简短。我发现在很多情况下,辅助函数可以替换为默认参数:

      (define (cpermit_1 sl (outl '()) (len (length sl)))
        (cond ((< len 1) outl)
              (else (define sl2 (append (rest sl) (list (first sl))))
                    (cpermit_1 sl2 (cons sl2 outl) (sub1 len)))))
      

      输出是:

      (cpermit_1 '(1 2 3 4))
      '((1 2 3 4) (4 1 2 3) (3 4 1 2) (2 3 4 1))
      

      【讨论】:

        猜你喜欢
        • 2010-11-02
        • 1970-01-01
        • 1970-01-01
        • 2014-05-13
        • 1970-01-01
        • 2020-03-21
        • 1970-01-01
        • 1970-01-01
        • 2019-01-27
        相关资源
        最近更新 更多