Clojure:如何优雅地在地图中移动矢量元素

时间:2017-02-27 02:32:36

标签: clojure clojurescript

在clojure中,我试图完成以下逻辑:

Input:
{:a [11 22 33] :b [10 20 30]}, 2
Output:
{:a [11] :b [10 20 30 22 33]}

即。将最后2个元素从:a移动到:b

这种手术有什么方法吗?

6 个答案:

答案 0 :(得分:2)

由于您正在有效地修改地图中的两个映射,因此最简单的方法是显式解构地图并通过文字返回新地图,使用subvecinto进行矢量操作:

id (or) name

作为奖励,这个特殊的实现既非常惯用又非常快。

或者,使用here中的(defn move [m n] (let [{:keys [a b]} m i (- (count a) n) left (subvec a 0 i) right (subvec a i)] {:a left :b (into b right)})) (move {:a [11 22 33] :b [10 20 30]} 2) ;;=> {:a [11], :b [10 20 30 22 33]} 函数,您可以这样写:

split-at'

答案 1 :(得分:2)

首先,当要移动的元素数量大于集合的大小时,在其他答案中使用sub-vec将抛出IndexOutOfBoundsException。

其次,解构,大多数人在这里做的方式,将功能耦合到一个特定的数据结构。这是一个带有键的映射:a和:b以及作为向量的这些键的值。现在,如果您更改输入中的一个键,则还需要在移动函数中更改它。

我的解决方案如下:

(defn move [colla collb n]
  (let [newb (into (into [] collb) (take-last n colla))
        newa (into [] (drop-last n colla))]
       [newa newb]))

这适用于任何集合,并将返回2个向量的向量。我的解决方案更可重用。尝试:

(move (range 100000) (range 200000) 10000)

编辑:

现在,您可以使用第一个和第二个来访问返回中所需的向量。

答案 2 :(得分:1)

我会做的只是与乔希有点不同:

(defn tx-vals [ {:keys [a b]} num-to-move ]
  {:a (drop-last num-to-move a)
   :b (concat b (take-last num-to-move a)) } )

(tx-vals {:a [11 22 33], :b [10 20 30]} 2) 
      => {:a (11),       :b (10 20 30 22 33)}

更新

有时使用clojure.core/split-at函数可能更方便,如下所示:

(defn tx-vals-2 [ {:keys [a b]} num-to-move ]
  (let [ num-to-keep        (- (count a) num-to-move)
        [a-head, a-tail]   (split-at num-to-keep a) ]
    { :a a-head
     :b (concat b a-tail) } ))

如果输出首选(我最喜欢的!),请执行以下操作:

(defn tx-vals-3 [ {:keys [a b]} num-to-move ]
  (let [ num-to-keep        (- (count a) num-to-move)
         [a-head, a-tail]   (split-at num-to-keep a) ]
    {:a (vec a-head)
     :b (vec (concat b a-tail))} ))

获得结果:

(tx-vals-2 data 2) => {:a (11), :b (10 20 30 22 33)}
(tx-vals-3 data 2) => {:a [11], :b [10 20 30 22 33]}

答案 3 :(得分:0)

(defn f [{:keys [a b]} n]
  (let [last-n (take-last n a)]
    {:a (into [] (take (- (count a) n) a))
     :b (into b last-n)}))

(f {:a [11 22 33] :b [10 20 30]} 2)
=> {:a [11], :b [10 20 30 22 33]}

答案 4 :(得分:0)

如果这些项目的顺序无关紧要,这是我的尝试:

(def m {:a [11 22 33] :b [10 20 30]})

(defn so-42476918 [{:keys [a b]} n]
  (zipmap [:a :b] (map vec (split-at (- (count a) n) (concat a b)))))

(so-42476918 m 2)

给出:

{:a [11], :b [22 33 10 20 30]}

答案 5 :(得分:0)

我会选择一种方法,它与之前的答案略有不同(从技术上来说,它是相同的,但它在应用程序级别上有所不同)。

首先,在两个集合之间传输数据是一项非常常见的任务,因此它至少应该为您的库中的一些特殊实用功能提供:

(defn transfer [from to n & {:keys [get-from put-to]
                             :or {:get-from :start :put-to :end}}]
  (let [f (if (= get-from :end)
            (partial split-at (- (count from) n))
            (comp reverse (partial split-at n)))
        [from swap] (f from)]
    [from (if (= put-to :start)
            (concat swap to)
            (concat to swap))]))

好吧,它看起来很冗长,但它允许您将数据从一个集合的开头/结尾传输到另一个集合的开始/结束:

user> (transfer [1 2 3] [4 5 6] 2)
[(3) (4 5 6 1 2)]

user> (transfer [1 2 3] [4 5 6] 2 :get-from :end)
[(1) (4 5 6 2 3)]

user> (transfer [1 2 3] [4 5 6] 2 :put-to :start)
[(3) (1 2 4 5 6)]

user> (transfer [1 2 3] [4 5 6] 2 :get-from :end :put-to :start)
[(1) (2 3 4 5 6)]

所以剩下的就是让你的域名特定功能在其上:

(defn move [data n]
  (let [[from to] (transfer (:a data) (:b data) n
                            :get-from :end
                            :put-to :end)]
    (assoc data
           :a (vec from)
           :b (vec to))))

user> (move {:a [1 2 3 4 5] :b [10 20 30 40] :c [:x :y]} 3)
{:a [1 2], :b [10 20 30 40 3 4 5], :c [:x :y]}