【问题标题】:Clojure idiomatic way to update multiple values of mapClojure 惯用的方式来更新地图的多个值
【发布时间】:2015-09-21 02:51:27
【问题描述】:

这可能很简单,但我就是无法克服它。 我有一个嵌套映射的数据结构,如下所示:

(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 #(def m (assoc-in m [% %] 0)) v) 会起作用,但在map 的函数内使用def 似乎不正确。 将 m 变成原子版本并使用 swap! 似乎更好。但并不多它似乎也很慢。

(def am (atom m))
(map #(swap! am assoc-in[% %] 0) v)

最好/正确的方法是什么?

更新

这里有一些很棒的答案。我在Clojure: iterate over map of sets 发布了一个后续问题,与这个问题密切相关,但没有那么多。

【问题讨论】:

标签: dictionary clojure reduce


【解决方案1】:

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

现在我们已经介绍了该做什么,让我们看看如何去做 ;-)。

您可以采取多种方法。对于来自命令式范式的人来说,最简单的开始可能是loop

(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'

但是,loop 是功能范式中相对较低级别的构造。这里我们可以注意到一个模式:我们循环遍历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

此表单要短一些,因为它不需要loop 表单所需的样板代码。 reduce 表单一开始可能不那么容易阅读,但是一旦你习惯了函数式代码,它就会变得更加自然。

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

(swap! am update-m v)

【讨论】:

  • 女士们,先生们,这就是您回答问题的方式。
【解决方案2】:

当然不推荐在函数中使用def。 OO看待问题的方法是查看数据结构内部并修改值。看待问题的功能性方法是建立一个新的数据结构,代表旧的数据结构改变了值。所以我们可以做这样的事情

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

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

【讨论】:

    【解决方案3】:

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

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

    【讨论】:

    • 但这不是忽略 OPs 请求提供要设置为 0 的键列表吗?
    • @cfrick 我不清楚向量是否必要。 OP 的文字要求是“将 every m[i][i] 设置为 0。”
    • 肯定不是,如果v 总是(keys m)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-05-17
    • 1970-01-01
    • 1970-01-01
    • 2013-03-08
    • 1970-01-01
    • 2014-03-26
    • 1970-01-01
    相关资源
    最近更新 更多