Clojure:将函数应用于地图的叶节点

时间:2016-01-29 17:08:40

标签: clojure

如何将函数应用于(嵌套)映射的叶节点?例如,让我们有这张地图:

{:a 1
 :b {:c 2}
 :d [{:e 3} {:f 4}]}

假设我们想要增加此映射中的所有叶节点并产生以下结果:

clojure.zip

目前,我正在考虑使用this answer中的map-zipper并通过leaf-node?函数编辑地图。但是,我不知道如何遍历拉链以及如何识别叶节点。我应该看一下什么功能?有没有拉链的更简单的解决方案吗?

是否可以执行以下工作(假设有一个(require '[clojure.zip :as zip]) (defn inc-leaf-nodes [loc] (if (zip/end? loc) (zip/node loc) (recur (zip/next (if (leaf-node? loc) (zip/edit loc inc) loc))))) 谓词测试拉链中的位置是否为叶节点?)

{{1}}

1 个答案:

答案 0 :(得分:4)

可以在给定叶子的适当定义的情况下实现leaf-node?函数,但建议的inc-leaf-nodes至少存在两个问题:

  • 您链接到的拉链实现使用地图条目作为节点,因此您不能简单地将inc传递给zip/edit - 您必须将其包装在将应用它的辅助函数中价值立场;

  • 拉链也不会将非地图值视为分支,因此它不会下降到向量[{:e 3} {:f 4}] - 您需要一个不同的拉链来克服这个问题。

假设叶子不是地图,集合或顺序集合,而映射函数仅应用于地图中的值位置,您可以定义map-leaves函数,如下所示: / p>

(defn map-leaves [f x]
  (cond
    (map? x) (persistent!
               (reduce-kv (fn [out k v]
                            (assoc! out k (map-leaves f v)))
                 (transient {})
                 x))
    (set? x) (into #{} (map #(map-leaves f %)) x)
    (sequential? x) (into [] (map #(map-leaves f %)) x)
    :else (f x)))

在REPL:

(map-leaves inc {:a 0 :b {:c 1 :d [{:e 2} {:f 3}]}})
;= {:a 1, :b {:c 2, :d [{:e 3} {:f 4}]}}

(map-leaves inc {:a 0 :b {:c 1 :d [{:e 2} {:f 3} {:g #{1 2 3}}]}})
;= {:a 1, :b {:c 2, :d [{:e 3} {:f 4} {:g #{4 3 2}}]}}