Algorithm 方案中的循环置换

Algorithm 方案中的循环置换,algorithm,recursion,scheme,racket,circular-permutations,Algorithm,Recursion,Scheme,Racket,Circular Permutations,您好,我尝试使用递归在Scheme(Dr.Racket)中进行循环置换 例如,如果我们有(1233),循环置换给出((1231)(3112)) 我写了一段代码,但我有一个问题,使转变 我的代码: (define cpermit (lambda (lst) (cpermitAux lst (length lst)))) (define cpermitAux (lambda (lst n) (if (zero? n) '() (append (cpermit

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

例如,如果我们有(1233),循环置换给出((1231)(3112))

我写了一段代码,但我有一个问题,使转变

我的代码:

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

您可以使用移动列表的函数

(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 '()))))))
以下代码也可以工作(没有任何辅助函数):

输出为:

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

这个答案是@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,您可以将
n
sl
outl
视为实现了与“可变变量”相同的目的,但这实际上与我之前将
循环
定义为递归辅助函数时编写的代码相同

上述模式对于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))
您可能已经注意到,外部列表的
(列表1 2 3 4)
最后一个,
(列表2 3 4 1)
倒数第二个,等等。这是因为我们通过使用
cons
预挂起列表来构建列表。要解决这个问题,我们可以在最后将其反转

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

下面的解决方案是功能性的和简短的。我发现在许多情况下,可以用默认参数替换助手函数:

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

这里的问题是,您实际上从未对
lst
执行任何会重新排列其元素的操作。唯一实际使用它的地方(而不是通过调用
cpermitAux
将其线程化)是
(cons lst'())
,它只使用
lst
,不做任何修改。递归调用也只传递
lst
而不进行修改,因此您所做的就是实现一个
replicate
函数。Thx!我添加了一个旋转函数,现在它可以工作了。这很好,但是“没有任何辅助函数”不应该成为目标。Helper函数是一件好事,特别是如果它们有明确的目的那么,你应该避免
set,因为命令式代码可以禁用优化。相反,您可以使用递归助手函数。如果需要,您可以在
cpermit
函数体中定义helper函数,我将发布一个将您的答案转换为该模式的答案
(cpermit_1 '(1 2 3 4))
'((1 2 3 4) (4 1 2 3) (3 4 1 2) (2 3 4 1))