【问题标题】:How do I find then update values in maps in a series of vectors in Clojure?如何在 Clojure 中的一系列向量中找到然后更新映射中的值?
【发布时间】:2021-04-02 05:24:08
【问题描述】:

在如下数据结构中找到:fooba4 值的优雅方法是:

[[{:foo "ba1"}{:foo "ba2"}] [{:foo "ba3"}{:foo "ba4"}]]

并添加 :bif "baf" 键和值,以便包含我所寻找的键/值的映射:

[[{:foo "ba1"}{:foo "ba2"}] [{:foo "ba3"}{:foo "ba4" :bif "baf"}]]?

这是我要弄清楚的主要问题;我确实想知道如果有多个级别的嵌套地图,你会如何做到这一点,但这将是我接下来要理解的。

我认为,我已经看到了解决这类问题的各种库,我完全不反对使用它们,尽管它们必须在 ClojureScript 中工作。

我正在尝试更新 Reagent atom,以便 UI 元素上的类会发生变化。

【问题讨论】:

    标签: clojure clojurescript


    【解决方案1】:

    (我从Rulle's solution那里学到了一些关于decoratorsmerge的知识。)

    您所描述的是walking 嵌套数据结构。 Postwalking 或 prewalking 获取嵌套数据结构的 every possible subform 并对它应用函数。

    以下是我使用 postwalk 获得所需解决方案的方法(注意:assoc 向地图添加键值):

    (ns walking.core
      (:require [clojure.walk :as w]))
    
    (clojure.walk/postwalk #(if (and (map? %) (= (:foo %) "ba4")) (assoc % :bif "baf") %) 
      [[{:foo "ba1"} {:foo "ba2"}] [{:foo "ba3"} {:foo "ba4"}]])
    ;;=> [[{:foo "ba1"} {:foo "ba2"}] [{:foo "ba3"} {:foo "ba4", :bif "baf"}]]
    

    无论它如何嵌套,它都可以工作:

    (clojure.walk/postwalk #(if (and (map? %) (= (:foo %) "ba4")) (assoc % :bif "baf") %) 
      [[{:foo "ba1"} {:foo "ba2"}] [[{:foo "ba3"} {:test {:foo "ba4"}}]]])
    ;;=> [[{:foo "ba1"} {:foo "ba2"}] [[{:foo "ba3"} {:test {:foo "ba4", :bif "baf"}}]]]
    

    为什么是postwalk 而不是prewalk?两者都有效。

    (clojure.walk/prewalk #(if (and (map? %) (= (:foo %) "ba4")) (assoc % :bif "baf") %) 
      [[{:foo "ba1"} {:foo "ba2"}] [{:foo "ba3"} {:foo "ba4"}]])
    ;;=> [[{:foo "ba1"} {:foo "ba2"}] [{:foo "ba3"} {:foo "ba4", :bif "baf"}]]
    

    您不必lein install 任何东西,它是 Clojure 的一部分;你只需要它。

    【讨论】:

    • 这也太棒了!我喜欢不必在您的解决方案(您的和 Rulle 的)中添加依赖项。我也一直对库感兴趣,但我应该总是尽可能地使用现有的 Clojure。
    • 顺便说一句,超级喜欢嵌套地图的工作方式!
    【解决方案2】:

    Clojure 非常方便地为这类事物定义迷你语言。通用且相当可重用的解决方案不必比专门的解决方案长得多。如果您对使用库不感兴趣,这里有一个建议。

    本质上,您需要一个函数来更新数据结构。这个函数可以使用作为构建块的相同类型的基本函数来表达,构成我之前提到的一种迷你语言。在此示例中,构建块将是 vector-scannermap-decorator。我们将这些函数组合成my-path,它将执行完整更新。

    ;; Path building blocks
    (defn vector-scanner [subpath]
      #(mapv subpath %))
    
    (defn map-decorator [k v decoration]
      #(if (and (map? %) (= v (k %)))
         (merge % decoration)
         %))
    
    ;; Function to do the update
    (def my-path (-> (map-decorator :foo "ba4" {:bif "baf"})
                     vector-scanner
                     vector-scanner))
    
    ;; Running it
    (my-path [[{:foo "ba1"}{:foo "ba2"}] [{:foo "ba3"}{:foo "ba4"}]])
    ;; => [[{:foo "ba1"} {:foo "ba2"}] [{:foo "ba3"} {:foo "ba4", :bif "baf"}]]
    

    这种方法在本质上与transducers 可以在各种配置中组合的方式非常相似。

    【讨论】:

    • 刚学会what decorators are。称它为装饰器是一种很好的表达方式。
    • 谢谢你!正如 Aaron 提到的,很高兴看到正在使用的“装饰器”模式。 Clojure 可以实现所有已经学过的东西,这真是太酷了。
    • 旁注:我真的不知道如何处理应该点击的“接受的答案”。不点击它让我觉得我不欣赏。另一方面,当每个答案都是不同的方法时,选择答案似乎很奇怪。我希望他们没有这个功能。我总是想知道如果不选择某人的答案是否会使他们不太可能回答。这在函数式编程场景中不太可能。我会点击 Aaron 的,因为他们的递归工作,但你的对我来说同样有价值。请继续回答! :-)
    猜你喜欢
    • 2016-07-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多