clojure - 有序的2个列表的成对组合

时间:2012-03-28 07:47:01

标签: list clojure combinations

对于clojure来说还是一个新手,我仍然在努力解决它的问题。如果我有2个列表,说“1234”和“abcd”我需要制作长度为4的所有可能的有序列表。我想要的长度为4的输出是:

("1234" "123d" "12c4" "12cd" "1b34" "1b3d" "1bc4" "1bcd" 
 "a234" "a23d" "a2c4" "a2cd" "ab34" "ab3d" "abc4" "abcd")

其中2 ^ n的数量取决于输入。

我编写了以下函数来通过随机遍历生成单个字符串/列表。  参数[par]就像[“1234”“abcd”]

(defn make-string [par] (let [c1 (first par) c2 (second par)] ;version 3 0.63 msec
  (apply str (for [loc (partition 2 (interleave c1 c2)) 
                   :let [ch (if (< (rand) 0.5) (first loc) (second loc))]] 
                     ch))))

输出将是上面16个有序列表中的1个。两个输入列表中的每一个将始终具有相等的长度,例如2,3,4,5,最多说2 ^ 38或在可用的ram内。在上面的函数中,我试图修改它以生成所有有序列表但失败了。希望有人可以帮助我。感谢。

6 个答案:

答案 0 :(得分:7)

Mikera是正确的,你需要使用递归,但你可以做到这一点,同时更简洁,更一般 - 为什么使用两个字符串,当你可以使用N序列?

(defn choices [colls]
  (if (every? seq colls)
    (for [item (map first colls)
          sub-choice (choices (map rest colls))]
      (cons item sub-choice))
    '(())))

(defn choose-strings [& strings]
  (for [chars (choices strings)]
    (apply str chars)))

user> (choose-strings "123" "abc")
("123" "12c" "1b3" "1bc" "a23" "a2c" "ab3" "abc")

这种递归嵌套for是一种非常有用的模式,用于通过选择的“树”创建一系列路径。无论是实际的树,还是反复重复的相同选择,或者(如此处)一组不依赖于先前选择的N个选项,这是一个可用的便利工具。

答案 1 :(得分:6)

您还可以利用clojure.math.combinatorics包中的cartesian-product,但这需要对数据进行转换前后的转换:

(ns your-namespace (:require clojure.math.combinatorics))

(defn str-combinations [s1 s2]
     (->>
        (map vector s1 s2) ; regroup into pairs of characters, indexwise
        (apply clojure.math.combinatorics/cartesian-product) ; generate combinations
        (map (partial apply str))))  ; glue seqs-of-chars back into strings

> (str-combinations "abc" "123")
("abc" "ab3" "a2c" "a23" "1bc" "1b3" "12c" "123")
>

答案 2 :(得分:4)

诀窍是使函数递归,在每一步调用自己的列表的其余部分。

您可以执行以下操作:

(defn make-all-strings [string1 string2]
  (if (empty? string1) 
    [""]
    (let [char1 (first string1)
          char2 (first string2)
          following-strings (make-all-strings (next string1) (next string2))]
      (concat 
        (map #(str char1 %) following-strings)
        (map #(str char2 %) following-strings)))))

(make-all-strings "abc" "123")
=> ("abc" "ab3" "a2c" "a23" "1bc" "1b3" "12c" "123")

答案 3 :(得分:2)

(defn combine-strings [a b]
  (if (seq a)
    (for [xs (combine-strings (rest a) (rest b))
          x [(first a) (first b)]]
      (str x xs))
    [""]))

现在我写了它,我意识到它是一个不那么通用的版本。

答案 4 :(得分:1)

您还可以使用0到16之间数字的二进制数字来形成组合:
如果一个位为零,则从第一个字符串中选择,否则从第二个字符

E.g。 6 = 2r0110 =&gt; “1bc4”,13 = 2r1101 =&gt; “ab3d”等。

(map (fn [n] (apply str (map #(%1 %2)
                             (map vector "1234" "abcd")
                             (map #(if (bit-test n %) 1 0) [3 2 1 0])))); binary digits
     (range 0 16))
=> ("1234" "123d" "12c4" "12cd" "1b34" "1b3d" "1bc4" "1bcd" "a234" "a23d" "a2c4" "a2cd" "ab34" "ab3d" "abc4" "abcd")

同样的方法可以应用于从两个以上的字符串生成组合 假设您有3个字符串(“1234”“abcd”“ABCD”),将有81个组合(3 ^ 4)。使用base-3三进制数字:

(defn ternary-digits [n] (reverse (map #(mod % 3) (take 4 (iterate #(quot % 3) n))))
(map (fn [n] (apply str (map #(%1 %2)
                             (map vector "1234" "abcd" "ABCD")
                             (ternary-digits n)
     (range 0 81))

答案 5 :(得分:0)

(def c1 "1234")
(def c2 "abcd")

(defn make-string [c1 c2]
  (map #(apply str %)
       (apply map vector
              (map (fn [col rep]
                     (take (math/expt 2 (count c1))
                           (cycle (apply concat
                                         (map #(repeat rep %) col)))))
                   (map vector c1 c2)
                   (iterate #(* 2 %) 1)))))

(make-string c1 c2)
=> ("1234" "a234" "1b34" "ab34" "12c4" "a2c4" "1bc4" "abc4" "123d" "a23d" "1b3d" "ab3d" "12cd" "a2cd" "1bcd" "abcd")