2014-12-21 38 views
0

我有两个地图包含项目:添加收藏从1项集2,如果收集2不从收集1

(def people {:1 "John" :2 "Paul" :3 "Ringo" :4 "George"})

(def band 
    {:data 
    {:members 
     {:1 {:id 1 :name "John"} 
     :2 {:id 2 :name "Paul"}}}}) 

我要循环people并添加这不[:data :members]存在band任何成员,导致:

(def band 
    {:data 
    {:members 
     {:1 {:id 1 :name "John"} 
     :2 {:id 2 :name "Paul"} 
     :3 {:id 3 :name "Ringo"} 
     :4 {:id 4 :name "George"}}}}) 

这是我已经试过:

(for [[id name] people] 
    (when-not 
    (contains? (get-in band [:data :members]) id) 
    (assoc-in band [:data :members id] {:id id :name name}))) 

其中产量:

({:data 
    {:members 
    {:4 {:id :4, :name "George"}, 
     :1 {:name "John", :id 1}, 
     :2 {:name "Paul", :id 2}}}} 
nil 
nil 
{:data 
    {:members 
    {:1 {:name "John", :id 1}, 
     :2 {:name "Paul", :id 2}, 
     :3 {:id :3, :name "Ringo"}}}}) 

我不知道为什么我找回什么看起来是band每个突变的列表。我在这里做错了什么?我如何将people的缺失成员添加到band [:data :members]

回答

3

要迂腐你不回任何带变异。事实上,Clojure最重要的特性之一是标准类型是不可变的,主要的收集操作返回一个修改过的拷贝而不会改变原始的。

另外,for在Clojure中不是一个循环,它是一个列表理解。这就是为什么它总是返回每一步的序列。因此,不是一次一步更改输入,而是针对每个步骤的输入做出新的变化,每个步骤都来自不可变的原始输入。

基于值序列对输入进行一系列更新副本的标准构造是reduce,它将累加器的新版本和列表的每个元素传递给函数。

最后,你误解了:keyword语法的作用 - 为了构造映射键而不需要为一个项添加前缀:几乎任何clojure值都是映射的有效键,而关键字只是一个方便成语。

user=> (def band 
      {:data 
       {:members 
       {1 {:id 1 :name "John"} 
       2 {:id 2 :name "Paul"}}}}) 
#'user/band 
user=> (def people {1 "John" 2 "Paul" 3 "Ringo" 4 "George"}) 
#'user/people 
user=> (pprint 
     (reduce (fn [band [id name :as person]] 
        (if-not (contains? (get-in band [:data :members]) id) 
         (assoc-in band [:data :members id] {:id id :name name}) 
         band)) 
        band 
        people)) 
{:data 
{:members 
    {3 {:id 3, :name "Ringo"}, 
    4 {:id 4, :name "George"}, 
    1 {:name "John", :id 1}, 
    2 {:name "Paul", :id 2}}}} 
nil 

您可能会注意到fn传递给reduce的身体是基本相同的for理解的身体。不同之处在于,代替when-not,在替代情况下返回nil,我使用if-not,它允许我们传播累加器(这里称为band,与输入相同),而不管它是否有任何新版本。

+0

感谢您的详细解释。我并没有意识到“什么时候不行”和“如果不行”的区别 - 非常有帮助! –

+0

在函数签名中关于'[id name:as person]'的问题 - 你是否包含':as person'来说明'id'和'name'是一个人的属性?这是一个成语吗?我认为这绝对可以使代码的意图清晰。 –

+1

是的,你也可以在之后引用'person'来表示整个集合,但是我只是把它包含来帮助传达意图(我喜欢给一个:作为解构的名称,除非我有一个令人信服的理由不要) – noisesmith