我正在尝试在Racket中实现一种算法(不使用循环),生成元组,它消耗长度为 m 的列表和自然数为 n ,产生该列表中元素的m个集合的所有可能的n元组。例如:
(check-expect
(generate-tuples '(+ -) 3)
'((+ + +) (+ + -) (+ - +) (+ - -)
(- + +) (- + -) (- - +) (- - -)))
我很难提出一个实用的解决方案。我已经实现了一种算法,该算法可以生成给定m集的所有可能排列:
(define (generate-permutations lst)
(cond [(empty? lst) empty]
[(empty? (rest lst)) (list lst)]
[else
(local [(define (split left mid right)
(append
(map (lambda (x) (cons mid x))
(generate-permutations (append left right)))
(cond [(empty? right) empty]
[else (split (cons mid left)
(first right) (rest right))])))]
(split empty (first lst) (rest lst)))]))
这可行,但是我不知道我是否应该在生成元组的解决方案中尝试使用它。我还想出了一个辅助函数,它只处理“平凡情况”(对于生成元组),即对于列表'(+-/)和自然数3,它将生成'(+++ +), '(---)和'(/ / /):
(define (trivial-cases lst n)
(cond [(empty? lst) empty]
[else (cons (build-list n (lambda (x) (first lst)))
(trivial-cases (rest lst) n))]))
总的来说,元组是有序的,并且列表长度取决于 n ,这一事实最让我失望。
答案 0 :(得分:2)
这是一个可能的解决方案:
(define (append-all lst lst-of-lst)
(if (empty? lst)
'()
(append (map (lambda (l) (cons (car lst) l)) lst-of-lst)
(append-all (cdr lst) lst-of-lst))))
(define (generate-tuples lst n)
(cond ((= n 0) '())
((= n 1) (map list lst))
(else (let ((tuples (generate-tuples lst (- n 1))))
(append-all lst tuples)))))
(generate-tuples '(+ -) 3)
'((+ + +) (+ + -) (+ - +) (+ - -) (- + +) (- + -) (- - +) (- - -))
函数append-all
获取一个列表和一个列表列表,然后依次将第一个列表的每个元素与第二个参数的所有列表连接起来,并返回结果列表。
对于n = 0,第二个函数的终止条件只是空列表,对于n = 1,则是由包含第一个参数元素的单例列表构成的列表。当它重复出现时,首先会生成带有n-1个元素的结果,然后在所有这些列表的开头串联第一个参数的所有元素,进而调用append-all。