2015-11-08 42 views
5

我正在尋找類似於clojure.walk有一個inner函數,它作爲自變量的函數走:Clojure的 - 與路徑

  • 沒有一個鍵和一個值,因爲是用Clojure的情況。步行/步行功能
  • 但是訪問頂級數據結構中的值所需的鍵向量。
  • 遞歸地遍歷所有數據

實施例:

;; not good since it takes `[k v]` as argument instead of `[path v]`, and is not recursive. 
user=> (clojure.walk/walk (fn [[k v]] [k (* 10 v)]) identity {:a 1 :b {:c 2}}) 
;; {:a 10, :c 30, :b 20} 

;; it should receive as arguments instead : 
[[:a] 1] 
[[:b :c] 2] 

注:

  • 它應與陣列工作太,使用鍵0,1,2 ... (就像在get-in中那樣)。
  • 我真的不關心outer參數,如果允許以簡化代碼。

回答

7

目前學習Clojure的,我想這是一個鍛鍊。 但是,我發現直接實現它很難,因爲它沿着應用內部函數的樹走下去。

要實現你正在尋找的結果,我在2拆分任務:

  • 首先變換嵌套結構到字典中的路徑爲重點,和值,
  • 然後映射內部功能結束,或減少與外部功能。

我的實現:

;; Helper function to have vector's indexes work like for get-in 
(defn- to-indexed-seqs [coll] 
    (if (map? coll) 
    coll 
    (map vector (range) coll))) 

;; Flattening the tree to a dict of (path, value) pairs that I can map over 
;; user> (flatten-path [] {:a {:k1 1 :k2 2} :b [1 2 3]}) 
;; {[:a :k1] 1, [:a :k2] 2, [:b 0] 1, [:b 1] 2, [:b 2] 3} 
(defn- flatten-path [path step] 
    (if (coll? step) 
    (->> step 
     to-indexed-seqs 
     (map (fn [[k v]] (flatten-path (conj path k) v))) 
     (into {})) 
    [path step])) 

;; Some final glue 
(defn path-walk [f coll] 
    (->> coll 
     (flatten-path []) 
     (map #(apply f %)))) 

;; user> (println (clojure.string/join "\n" (path-walk #(str %1 " - " %2) {:a {:k1 1 :k2 2} :b [1 2 3]}))) 
;; [:a :k1] - 1 
;; [:a :k2] - 2 
;; [:b 0] - 1 
;; [:b 1] - 2 
;; [:b 2] - 3 
+1

在您的例子,不應該將數據是'{[:一個:K1] 1,[:一個:K2] 2,[:B 0,] 1,[ :b 1] 2,[:b 2] 3}'? – nha

+0

糟糕! 有中到索引seqs錯誤, 應該是: (圖矢量(範圍)COLL) 代替: (地圖矢量科爾(範圍)) 我會更新我的答案:) – kimsnj

+0

謝謝 !我接受了你的答案,但仍然希望有一個不需要枚舉路徑的解決方案(我的用例是需要進行一些轉換的數據庫遷移)。 – nha

0

事實證明,司徒雷登哈洛韋發表a gist,可能是一些使用的(它使用的協議,這使得它擴展爲好):

(ns user) 

(def app 
    "Intenal Helper" 
    (fnil conj [])) 

(defprotocol PathSeq 
    (path-seq* [form path] "Helper for path-seq")) 

(extend-protocol PathSeq 
    java.util.List 
    (path-seq* 
    [form path] 
    (->> (map-indexed 
     (fn [idx item] 
      (path-seq* item (app path idx))) 
     form) 
     (mapcat identity))) 

    java.util.Map 
    (path-seq* 
    [form path] 
    (->> (map 
     (fn [[k v]] 
      (path-seq* v (app path k))) 
     form) 
     (mapcat identity))) 

    java.util.Set 
    (path-seq* 
    [form path] 
    (->> (map 
     (fn [v] 
      (path-seq* v (app path v))) 
     form) 
     (mapcat identity))) 


    java.lang.Object 
    (path-seq* [form path] [[form path]]) 

    nil 
    (path-seq* [_ path] [[nil path]])) 

(defn path-seq 
    "Returns a sequence of paths into a form, and the elements found at 
    those paths. Each item in the sequence is a map with :path 
    and :form keys. Paths are built based on collection type: lists 
    by position, maps by key, and sets by value, e.g. 

    (path-seq [:a [:b :c] {:d :e} #{:f}]) 

    ({:path [0], :form :a} 
    {:path [1 0], :form :b} 
    {:path [1 1], :form :c} 
    {:path [2 :d], :form :e} 
    {:path [3 :f], :form :f}) 
    " 
    [form] 
    (map 
    #(let [[form path] %] 
     {:path path :form form}) 
    (path-seq* form nil))) 

(comment 
    (path-seq [:a [:b :c] {:d :e} #{:f}]) 

    ;; finding nils hiding in data structures: 
    (->> (path-seq [:a [:b nil] {:d :e} #{:f}]) 
     (filter (comp nil? :form))) 

    ;; finding a nil hiding in a Datomic transaction 
    (->> (path-seq {:db/id 100 
        :friends [{:firstName "John"} 
          {:firstName nil}]}) 
     (filter (comp nil? :form))) 


) 

注意:在我的情況下,我也可以使用Specter,所以如果您正在閱讀本文,您可能也想查看它。