【问题标题】:clojure - ordered pairwise combination of 2 listsclojure - 2 个列表的有序成对组合
【发布时间】:2012-03-28 07:47:00
【问题描述】:

对于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 内。在上面的函数中,我尝试修改它以生成所有有序列表,但失败了。希望有人可以帮助我。谢谢。

【问题讨论】:

    标签: list clojure combinations


    【解决方案1】:

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

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

    【讨论】:

    • 感谢您的回答。这很棒,有助于改进我的 clojure。
    【解决方案2】:

    您还可以利用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")
    >
    

    【讨论】:

    • 从 python 论坛我想到了笛卡尔积,但不知道如何适应我的问题。感谢您提供此解决方案。
    • @Brian 您可能会从在 4clojure.com 上解决一些问题中受益——那里有很多与此类似的问题。检查其他用户的解决方案(例如,我认为是 Chris Houser 的 chouser)是获取有用的 Clojure 技巧的快速方法。
    【解决方案3】:

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

    你可以这样做:

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

    【讨论】:

    • 感谢您直接解决我的问题。它还有助于改进我的 clojure。
    【解决方案4】:
    (defn combine-strings [a b]
      (if (seq a)
        (for [xs (combine-strings (rest a) (rest b))
              x [(first a) (first b)]]
          (str x xs))
        [""]))
    

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

    【讨论】:

    • 喜欢这个:简单、时尚、优雅。看不出它怎么能更简单。
    【解决方案5】:

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

    例如6 = 2r0110 => "1bc4", 13 = 2r1101 => "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))
    

    【讨论】:

    • 一个优雅的解决方案。谢谢你。我一直在努力正确使用地图;你的功能肯定有帮助。
    【解决方案6】:
    (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")
    

    【讨论】:

    • 感谢您对这个问题的帮助。您的解决方案和其他解决方案大大扩展了我对 clojure 的认识。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-06-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多