简体   繁体   English

Clojure - 合并两个不同大小的向量

[英]Clojure - Merge two vectors of vectors different sizes

Here I am again facing some problems with Clojure.在这里,我再次面临 Clojure 的一些问题。 I have two vectors of vectors.我有两个向量向量。

[[a b c] [d e f] [g h i]]

and

[[a b] [d e] [g h] [j k]]

And I wanna merge these two in a way that the final vector would be something like this:我想以某种方式合并这两个,最终向量将是这样的:

 [[a b c] [d e f] [g h i] [j k l]]

In the output, the last item [jkl], the L is a constant value when there is no value to merge (because it has no corresponding item in the first vector. How can I do such thing?在输出中,最后一项[jkl],L在没有要合并的值时是一个常数值(因为它在第一个向量中没有对应的项。我该怎么做?

PS: I am new to Clojure and I appreciate a elaborated answer so that I could understand better. PS:我是 Clojure 的新手,我很欣赏详细的答案,以便我能更好地理解。 Also, sorry if this is a trivial question.另外,对不起,如果这是一个微不足道的问题。

In general:一般来说:

  • break the problem into separable parts将问题分解成可分离的部分
  • give things names给事物命名
  • compose the parts组成部分

So in this case your problem can be broken down into:因此,在这种情况下,您的问题可以分解为:

  • splitting the lists into the overlapping and non-overlapping parts将列表拆分为重叠和非重叠部分
  • choosing the best of each of the overlapping parts选择每个重叠部分中最好的
  • padding the non-overlapping parts to the correct length将非重叠部分填充到正确的长度
  • combining them back together.将它们重新组合在一起。

So if I make a couple assumptions about your problem here is an example of breaking it down and building it back up:因此,如果我对您的问题做出几个假设,这里有一个分解并重新构建它的示例:

user> (def a '[[a b c] [d e f] [g h i]])
#'user/a
user> (def b '[[a b] [d e] [g h] [j k]])
#'user/b

make a function to choose the correct pair of the overlapping parts.制作一个函数来选择正确的重叠部分对。 I chose length though you can merge these however you want:我选择了长度,尽管您可以根据需要合并这些:

user> (defn longer-list [x y]
        (if (> (count x) (count y))
          x
          y))
#'user/longer-list

make a function to pad out a list that's too short制作一个函数来填充一个太短的列表

user> (defn pad-list [l min-len default-value]
        (into l (take (- min-len (count l)) (repeat default-value))))
#'user/pad-list

Make a function that uses these two functions to split and then recombine the parts of the problem:制作一个函数,使用这两个函数进行拆分,然后重新组合问题的各个部分:

user> (defn process-list [a b]
        (let [a-len (count a)
              b-len (count b)
              longer-input (if (> a-len b-len)
                            a
                            b)
              shorter-input (if (< a-len b-len)
                            a
                            b)]
          (concat (map longer-list longer-input shorter-input)
                  (map #(pad-list % 3 'l) (drop (count shorter-input) longer-input)))))
#'user/process-list

and then test it :-)然后测试它:-)

user> (process-list a b)
([a b c] [d e f] [g h i] [j k l]) 

There are more details to work out, like what happens when the lists-of-lists are the same length, and if they are not subsets of each other.还有更多的细节需要解决,比如当列表的列表长度相同时会发生什么,如果它们不是彼此的子集。 (and yes you can smash this down to a "one liner" too) (是的,您也可以将其粉碎为“单衬”)

I'd take a look at clojure.core.matrix (see here );我会看看clojure.core.matrix (见这里); It has some nice operations which could help you with this.它有一些很好的操作可以帮助你解决这个问题。

i would generally go with the following approach:我通常会采用以下方法:

  1. fill collections up to the size of the longest one将集合填充到最长的集合的大小
  2. map both of them, filling every item of the collection up to the size of the longest, mapping items to select the resulting value.映射它们,将集合的每个项目填充到最长的大小,映射项目以选择结果值。

It is better to illustrate it with code:最好用代码来说明:

first of all let's make up some helper functions:首先让我们组成一些辅助函数:

(defn max-count [coll1 coll2] (max (count coll1) (count coll2)))

it's name says for itself.它的名字不言而喻。

(defn fill-up-to [coll size] (take size (concat coll (repeat nil))))

this one fills the collection with nil s up to some size:这个用nil填充集合到一定大小:

user> (fill-up-to [1 2 3] 10)
(1 2 3 nil nil nil nil nil nil nil)

now the merge function:现在合并功能:

(defn merge-colls [v1 v2 default-val]
  (let [coll-len (max-count v1 v2)
        comp-len (max-count (first v1) (first v2))]
    (mapv (fn [comp1 comp2]
            (mapv #(or %1 %2 default-val)
                  (fill-up-to comp1 comp-len)
                  (fill-up-to comp2 comp-len)))
          (fill-up-to v1 coll-len)
          (fill-up-to v2 coll-len))))

the outer mapv operates on collections made from initial parameters filled up to the length of the longest one ( coll-len ), so in context of the question it will be:外部mapv对由填充到最长参数( coll-len )长度的初始参数组成的集合进行操作,因此在问题的上下文中,它将是:

(mapv some-fn [[a b c] [d e f] [g h i] nil]]
              [[a b]   [d e]   [g h]   [j k]])

the inner mapv operates on inner vectors, filled up to the comp-len (3 in this case):内部 mapv 对内部向量进行操作,填充到comp-len (在这种情况下为 3):

(mapv #(or %1 %2 default-val) '[a b c] '[d e nil])
...
(mapv #(or %1 %2 default-val) '[nil nil nil] '[j k nil])

let's test it:让我们测试一下:

user> (let [v1 '[[a b c] [d e f] [g h i]]
            v2 '[[a b] [d e] [g h] [j k]]]
        (merge-colls v1 v2 'l))
[[a b c] [d e f] [g h i] [j k l]]

ok it works just as we wanted.好的,它就像我们想要的那样工作。

now if you look at the merge-colls , you may notice the repetition of the pattern:现在,如果您查看merge-colls ,您可能会注意到模式的重复:

(mapv some-fn (fill-up-to coll1 size)
              (fill-up-to coll2 size))

we can eliminate the duplication by moving this pattern out to a function:我们可以通过将这种模式移出一个函数来消除重复:

(defn mapv-equalizing [map-fn size coll1 coll2]
  (mapv map-fn (fill-up-to coll1 size) (fill-up-to coll2 size)))

and rewrite our merge:并重写我们的合并:

(defn merge-colls [v1 v2 default-val]
  (let [coll-len (max-count v1 v2)
        comp-len (max-count (first v1) (first v2))]
    (mapv-equalizing (fn [comp1 comp2]
                       (mapv-equalizing #(or %1 %2 default-val) 
                                        comp-len comp1 comp2))
                     coll-len v1 v2)))

test:测试:

user> (let [v1 '[[a b c] [d e f] [g h i]]
            v2 '[[a b] [d e] [g h] [j k]]]
        (merge-colls v1 v2 'l))
[[a b c] [d e f] [g h i] [j k l]]

ok.好的。 now we can shorten it by removing collection size bindings, as we need these values just once:现在我们可以通过删除集合大小绑定来缩短它,因为我们只需要一次这些值:

(defn merge-colls [v1 v2 default-val]
  (mapv-equalizing
   (partial mapv-equalizing
            #(or %1 %2 default-val)
            (max-count (first v1) (first v2)))
   (max-count v1 v2) v1 v2))

in repl:在回复:

user> (let [v1 '[[a b c] [d e f] [g h i]]
            v2 '[[a b] [d e] [g h] [j k]]]
        (merge-colls v1 v2 'l))
[[a b c] [d e f] [g h i] [j k l]]

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM