繁体   English   中英

Clojure:更新,但具有通配符和路径跟踪

[英]Clojure: update-in, but with wildcards and path tracking

我正在尝试更新由嵌套映射和序列组成的结构中的值,但是由于我想允许使用通配符update-in因此无法进行update-in 我的手动方法使我难看,大的,嵌套forinto {}调用。 我最终制作了一个带有结构的函数,类似选择器的序列和一个更新函数。

(defn update-each-in
  ([o [head & tail :as path] f]
   (update-each-in o path f []))
  ([o [head & tail :as path] f current-path]
   (cond
     (empty? path) (f o current-path)
     (identical? * head)
       (cond
         (map? o)
           (into {} (for [[k v] o]
             [k (update-each-in v tail f (conj current-path k))]))
         :else (for [[i v] (map-indexed vector o)]
           (update-each-in v tail f (conj current-path i))))
     :else (assoc o head
       (update-each-in (get o head) tail f (conj current-path head))))))

这使我可以简化对以下内容的更新

(def sample {"TR" [{:geometry {:ID12 {:buffer 22}}}
                   {:geometry {:ID13 {:buffer 33}
                               :ID14 {:buffer 55}}}
                   {:geometry {:ID13 {:buffer 44}}}]
             "BR" [{:geometry {:ID13 {:buffer 22}
                               :ID18 {:buffer 11}}}
                   {:geometry {:ID13 {:buffer 33}}}
                   {:geometry {:ID13 {:buffer 44}}}]})

(update-each-in sample [* * :geometry * :buffer]
  (fn [buf path] (inc buf)))

显然,这具有深度嵌套结构的堆栈溢出问题。 尽管我还没有达到那个目标,但是拥有一个强大的解决方案将是很好的。 谁能提出一个更简单/更快/更优雅的解决方案? 可以用减速器/换能器完成吗?

UPDATE要求更新函数还必须获取要更新的值的完整路径。

update-in具有与您创建的功能完全相同的签名,并且几乎具有相同的功能。 有两个区别:它不允许在“路径”中使用通配符,并且不会将中间路径传递给更新函数。

添加通配符以进行update-in

我已经从update-in源代码中改编了此代码

(defn update-in-*
    [m [k & ks] f & args]
        (if (identical? k *)
            (let [idx (if (map? m) (keys m) (range (count m)))]
                (if ks
                    (reduce #(assoc % %2 (apply update-in-* (get % %2) ks f args))
                            m
                            idx)
                    (reduce #(assoc % %2 (apply f (get % %2) args))
                            m
                            idx)))
            (if ks
                (assoc m k (apply update-in-* (get m k) ks f args))
                (assoc m k (apply f (get m k) args)))))

现在,这两行产生相同的结果:

(update-in-* sample [* * :geometry * :buffer] (fn [buf] (inc buf)))
(update-each-in sample [* * :geometry * :buffer] (fn [buf path] (inc buf)))

我对update-in的更改只是通过检查通配符。 如果遇到通配符,则必须修改该级别的每个子节点。 我使用reduce来保持对集合的累积更新。

另外,出于健壮性的考虑,我还要说一句:我尝试对通配符使用*以外的名称。 它可能作为映射中的键出现。

添加路径跟踪以进行update-in

如果要求更新功能接收完整路径,那么我将再修改一次update-in 函数签名更改并添加了(conj pk) ,仅此而已。

(defn update-in-*
    [m ks f & args] (apply update-in-*-with-path [] m ks f args))

(defn- update-in-*-with-path
    [p m [k & ks] f & args]
        (if (identical? k *)
            (let [idx (if (map? m) (keys m) (range (count m)))]
                (if ks
                    (reduce #(assoc % %2 (apply update-in-*-with-path (conj p k) (get % %2) ks f args))
                            m
                            idx)
                    (reduce #(assoc % %2 (apply f (conj p k) (get % %2) args))
                            m
                            idx)))
            (if ks
                (assoc m k (apply update-in-*-with-path (conj p k) (get m k) ks f args))
                (assoc m k (apply f (conj p k) (get m k) args)))))

现在,这两行产生相同的结果:

(update-in-* sample [* * :geometry * :buffer] (fn [path val] (inc val)))
(update-each-in sample [* * :geometry * :buffer] (fn [buf path] (inc buf)))

这比您原来的解决方案好吗? 我不知道。 我喜欢它,因为它是在update-in之后建模的,并且其他人可能在update-in比我自己更在意。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM