【问题标题】:How do I add an element to an array-map in Clojure?如何在 Clojure 中将元素添加到数组映射?
【发布时间】:2017-11-07 16:50:46
【问题描述】:

如何在 Clojure 中将元素添加到数组映射?我尝试使用 assoc 但它没有被添加?我基本上想为条目数组映射中的任何缺失项设置默认值 0。

(defn create-entry [doc]
 (let [entry (assoc doc "id" (str (java.util.UUID/randomUUID)))]
  (if (empty? (get entry "foo")) (assoc entry "foo" 0))
  (if (empty? (get entry "bar")) (assoc entry "bar" 0))))

在来自 Carcigenicate 的 cmets 之后更新:

(defn entry [doc]
(as-> (assoc doc "id" (str (java.util.UUID/randomUUID))) e
      (if (empty? (get e "foo")) (assoc e "foo" 0) e)
      (if (empty? (get e "bar")) (assoc e "bar" 0) e)))

(defn create-entry [doc]
  (prn (entry doc)))

【问题讨论】:

    标签: clojure


    【解决方案1】:

    您需要开始思考更多功能。请注意您使用的所有结构都是不可变的;他们自己永远不会改变。您的倒数第二行复制了entry,但您从不使用它;它只是被扔掉了。有几种方法可以处理这样的情况,您需要通过几个步骤来转换结构:

    • 只需使用let:

      (let [entry (assoc doc "id" (str (java.util.UUID/randomUUID)))
            def-foo (if (empty? (get entry "foo")) (assoc entry "foo" 0) entry)]
        (if (empty? (get def-foo "bar")) (assoc def-foo "bar" 0) def-foo)))
      

      注意最后一行如何使用def-foo 副本,而不是原来的entry

    • 使用线程宏:

          ; Create a new binding, e, that will hold the result of the previous form
          (as-> (assoc doc "id" (str (java.util.UUID/randomUUID))) e
                (if (empty? (get e "foo")) (assoc e "foo" 0) e)
                (if (empty? (get e "bar")) (assoc e "bar" 0) e))
      

      e 被之前的形式所取代。

    但请注意,如果您发现自己在同一个对象上使用了getassoc,您可能需要考虑使用update,这大大简化了一切,尤其是与-> 线程配对时宏:

    (-> (assoc doc "id" (str (java.util.UUID/randomUUID)))
        (update "foo" #(if (empty? %) 0 %))
        (update "bar" #(if (empty? %) 0 %)))
    

    我不得不对您的意图做出一些假设,因为您的代码有一个错误,直到我提交答案后我才注意到。在您的原始代码中,当条件为假时,您的 ifs 不会评估任何内容。我假设您只是不想在它们为假时更改任何内容。

    【讨论】:

    • 感谢您的详细回答。你可能会说我对 Clojure 很陌生。我已尝试使用线程宏来遵循您的答案,并使用代码更改更新了帖子。但是我不确定我是否正确解释了您的答案,因为我仍然无法打印 foo 或 bar 的默认值。
    • @Freid001 您需要通过将entry 括在括号中来调用它:(prn (entry doc))。请记住,符号必须是要调用的表单中的第一个元素。您的所有代码都在让我们打印有关函数本身的信息,因为您从未调用过它。
    • 谢谢 我可以看到默认值。我只是使用prn 将响应记录到控制台。
    • 正如我之前所说的,我是 Clojure 的新手,您可以推荐任何文档来帮助我更好地理解线程宏概念吗?
    • @Freid001 Np。 Clojure 文档非常棒。如果我是你,我会启动一个 REPL 并与他们混在一起。不过从-> 开始,因为它是最简单的。阅读它的文档,然后玩。你会明白的。
    【解决方案2】:

    为了补充 Carcigenicate 的回答,另一个建议是: 我会在默认地图上使用mergeassoc

    (merge {:default-1 123 :default-2 234} {:default-1 "foo"})
    => {:default-1 "foo", :default-2 234}
    

    请注意,merge 的参数顺序很重要,即最右边的地图优先于最左边的地图。您的 默认 地图值只有在未被其他地图覆盖的情况下才会“存活”。

    (def defaults {"foo" 0, "bar" 0})
    (defn create-entry [doc]
      (assoc defaults "id" (str (java.util.UUID/randomUUID))))
    (defn create-entry [doc]
      (merge defaults {"id" (str (java.util.UUID/randomUUID))}))
    

    在本例中使用assoc 具有相同的效果,我更喜欢那个版本。

    【讨论】:

      猜你喜欢
      • 2014-03-14
      • 2020-03-01
      • 2018-10-01
      • 1970-01-01
      • 2020-12-24
      • 2021-06-17
      • 1970-01-01
      • 2021-12-02
      • 2022-08-18
      相关资源
      最近更新 更多