【问题标题】:How to populate a hashmap through iteration or recursion in clojure?如何通过clojure中的迭代或递归填充哈希图?
【发布时间】:2022-10-17 21:13:04
【问题描述】:

我是 clojure 的新手,如果不像其他语言那样使用典型的 for 循环,我无法真正将头颅添加到 hashmap 中。例如,如果我有以下代码段:

(def empty-hashmap {})
(def big-hashmap (assoc empty-hashmap 1 2)) 

我将如何迭代并将 300 个单独的元素添加到大哈希图中?在这种情况下,我希望我的代码看起来像

(def empty-hashmap {})
(def big-hashmap (assoc empty-hashmap n (abundance n)))

其中 n 是数字 1 到 300,它将 300 个元素填充到大哈希图中。

【问题讨论】:

    标签: clojure


    【解决方案1】:

    正如 Alan Thompson 所说,reduce 是用于迭代序列并累积结果的通用工具。但是,如果您需要进行许多“独立”更改,例如在这里您将映射中的键与不依赖于键以外的任何值的值相关联,那么有更好的工具。 map 是基于旧序列生成新序列的通用工具,into 用于将序列转换为地图。所以,你可以写

    (into {}
          (map (fn [n] [n (abundance n)])
               (range 1 301)))
    

    请注意,(fn [n] [n (abundance n)]) 也可以写成(juxt identity abundance),尽管您会发现哪个更清楚。

    我个人不喜欢写(map (fn [n] ...)) - 通常如果你需要一个(单参数)lambda,for 是比map 更好的工具。 into/for 配对对于这样的任务非常常见:

    (into {}
          (for [n (range 1 301)]
            [n (abundance n)]))
    

    我完全不推荐使用atom 只是为了“更有必要的感觉”。有使用原子的好时机,但初学者不会很快遇到它们,这不是其中之一。

    【讨论】:

      【解决方案2】:

      只是想在@amalloy 的答案中添加一个reduce 代码示例:

        (let [keys [:a :b :c :d :e :f :g]
              vals [1  2  3  4  5  6  7]]
            (map vector keys vals))
      
        => ([:a 1] [:b 2] [:c 3] [:d 4] [:e 5] [:f 6] [:g 7])
      
        
        (let [keys [:a :b :c :d :e :f :g]
              vals [1  2  3  4  5  6  7]]
          (reduce
            (fn [a [k v]] (assoc a k v))
            {}
            (map vector keys vals)))
        => {:a 1, :b 2, :c 3, :d 4, :e 5, :f 6, :g 7}
      

      这使用带有“累加器功能”的reduce。对于每次迭代,函数都会使用映射的旧值和新的键值对来调用。它关联新值并返回带有额外键值对的新映射。初始值作为reduce{} 空映射)的第二个参数提供。

      【讨论】:

        【解决方案3】:

        Reduce 是一个不错的选择,从my favorite template project 开始:

        (ns tst.demo.core
          (:use demo.core tupelo.core tupelo.test))
        
        (verify
          (let [result (reduce  (fn [accum item]
                                  (assoc accum item (str "item " item)))
                         {} ; initial empty map
                         (range 5)) ; list of items to add
                         ]
            (is= result
              {0 "item 0"
               1 "item 1"
               2 "item 2"
               3 "item 3"
               4 "item 4"})))
        

        如果你想要一个更命令式的解决方案,你总是可以将结果累积到一个原子中:

        (verify
          (let [accum (atom {})]
            (doseq [idx (range 5)]
              (swap! accum
                #(assoc % idx (str "item " idx))))
            (is= @accum
              {0 "item 0"
               1 "item 1"
               2 "item 2"
               3 "item 3"
               4 "item 4"})))
        

        【讨论】:

        • reduce 在内部不使用原子。是什么让你认为它确实有效?
        • 是的,在这里包含 IReduce 太过分了,所以删除了评论。
        • 我不知道使用原子的 IReduce 实现,尽管我当然可以相信存在一个原子。你有指针吗?
        • 不,这都是带有可变变量的纯 Java。当您发布第一条评论时,我已经在查找详细信息(再次击败我!)。这是IPersistentVector.java的来源:github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/…
        • 啊,当然,在 Java 中你真的别无选择。我不认为我会将它与原子进行比较,甚至称它为可变的(尽管我同意,当然,本地正在被重新分配)。这只是 Java 编写loop 的方式。
        猜你喜欢
        • 1970-01-01
        • 2017-07-29
        • 2013-08-23
        • 2023-04-07
        • 2021-06-23
        • 1970-01-01
        • 2016-01-23
        • 2013-02-06
        • 1970-01-01
        相关资源
        最近更新 更多