Dictionary Clojure更新映射的多个值的惯用方法

Dictionary Clojure更新映射的多个值的惯用方法,dictionary,clojure,reduce,Dictionary,Clojure,Reduce,这可能很简单,但我就是受不了。 我有一个嵌套映射的数据结构,如下所示: (def m {:1 {:1 2 :2 5 :3 10} :2 {:1 2 :2 50 :3 25} :3 {:1 42 :2 23 :3 4}}) 我需要设置每个m[I][I]=0。这在非函数式语言中很简单,但我不能让它在Clojure上工作。考虑到我确实有一个包含所有可能值的向量,惯用的方法是如何做到这一点的?(我们称之为v) 执行(map#(defm(assoc in m[%%]0))v)将起作用,但在map上的函数

这可能很简单,但我就是受不了。 我有一个嵌套映射的数据结构,如下所示:

(def m {:1 {:1 2 :2 5 :3 10} :2 {:1 2 :2 50 :3 25} :3 {:1 42 :2 23 :3 4}})
我需要设置每个
m[I][I]=0
。这在非函数式语言中很简单,但我不能让它在Clojure上工作。考虑到我确实有一个包含所有可能值的向量,惯用的方法是如何做到这一点的?(我们称之为
v

执行
(map#(defm(assoc in m[%%]0))v)
将起作用,但在
map
上的函数中使用
def
似乎不正确。 将m转换成原子版本并使用
swap似乎更好。但也不是很慢

(def am (atom m))
(map #(swap! am assoc-in[% %] 0) v)
这样做的最佳/正确方式是什么

更新


这里有一些很棒的答案。我在这里发布了一个与此问题密切相关的后续问题。

当然不建议在函数中使用
def
。OO看待问题的方法是查看数据结构内部并修改值。从功能上看问题的方法是建立一个新的数据结构,该结构表示值发生变化的旧数据结构。所以我们可以这样做

(into {} (map (fn [[k v]] [k (assoc v k 0)]) m))

我们在这里所做的是映射
m
,与您在第一个示例中所做的方式大致相同。但是,我们没有修改
m
,而是返回键和值的
[]
元组。然后,我们可以将这个元组列表放回映射中。

您是对的,在函数中使用
def
是不好的。在
map
中使用具有副作用的函数(如
swap
)也是一种不好的形式。此外,
map
是惰性的,因此您的
map
/
swap
尝试实际上不会做任何事情,除非它被强制执行,例如,
dorun

现在我们已经讨论了不该做的事情,让我们来看看如何做;p> 你可以采取几种方法。对于来自命令式范例的人来说,最简单的开始可能是
循环

(defn update-m [m v]
  (loop [v' v
         m' m]
    (if (empty? v')
      ;; then (we're done => return result)
      m'
      ;; else (more to go => process next element)
      (let [i (first v')]
        (recur (rest v')                  ;; iterate over v
               (assoc-in m' [i i] 0)))))) ;; accumulate result in m'
然而,
循环
在功能范式中是一个相对低级的结构。这里我们可以注意到一种模式:我们在
v
的元素上循环,并在
m'
中累积变化。此模式由
reduce
功能捕获:

(defn update-m [m v]
  (reduce (fn [m' i]
            (assoc-in m' [i i] 0)) ;; accumulate changes
          m   ;; initial-value
          v)) ;; collection to loop over
此表单相当短,因为它不需要
循环所需的锅炉板代码。
reduce
表单一开始可能不那么容易阅读,但一旦您习惯了函数式代码,它就会变得更自然

现在我们有了
update-m
,我们可以使用它在程序中转换地图。例如,我们可以使用它来
交换一个原子。按照上面的例子:

(swap! am update-m v)

其他答案很好,但为了完整起见,这里有一个略短的版本,使用
进行理解。我觉得它更具可读性,但这是一个品味问题:

(into {} (for [[k v] m] {k (assoc v k 0)}))

女士们,先生们,这就是你们回答问题的方式。但这不是忽略OPs请求,提供要设置为0的密钥列表吗?@cfrick我不清楚向量是否必要。OP的字面要求是“将每m[i][i]设置为0”。如果
v
总是
(键m)
,则肯定不是。对于更复杂的情况,在更新深度嵌套结构时,Specter可能会感兴趣:|