在clojure中,我试图完成以下逻辑:
Input:
{:a [11 22 33] :b [10 20 30]}, 2
Output:
{:a [11] :b [10 20 30 22 33]}
即。将最后2个元素从:a移动到:b
这种手术有什么方法吗?
答案 0 :(得分:2)
由于您正在有效地修改地图中的两个映射,因此最简单的方法是显式解构地图并通过文字返回新地图,使用subvec
和into
进行矢量操作:
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]}