2016-05-13 2 views
0
私は動的にこのような構造のデータを変更する必要が

のための無限再帰をprewalk結合がベクトルの4番目の項目になることを確認してください。しかし、:list/itemsのすべてのインスタンスを(default :list/items [])に置き換える必要があります。Clojureのはdatomicクエリ

私がこれを行うために知っている唯一の方法は、clojure.walk/prewalkを使用することです。しかし、それは無限再帰につながる:徒歩:list/itemsを見つけて'(default :list/items [])に置き換えたら

(clojure.walk/prewalk #(if (= :list/items %) 
         '(default :list/items []) 
         %) 
         query) 

、それはその後、置き換え値で:list/itemsを見つけ、それを置き換えます。そういうことなど。

原子を使用して、値が1回だけ置換されることを確認できますが、不正行為のように感じます。

他の方法はありますか?あなたはおそらくpostwalkを使用する必要があり、この場合の

答えて

1

:によって

user> (clojure.walk/prewalk #(do (println %) 
           (if (= % 1) [10] %)) 
          [[1 2 3 [1 2]] [1 2]]) 
[[1 2 3 [1 2]] [1 2]] 
[1 2 3 [1 2]] 
1 
10 ;; goes deeper 
2 
3 
[1 2] 
1 
10 ;; and here 
2 
[1 2] 
1 
10 ;; and here 
2 
[[[10] 2 3 [[10] 2]] [[10] 2]] 

user> (clojure.walk/postwalk #(do (println %) 
            (if (= % 1) [10] %)) 
          [[1 2 3 [1 2]] [1 2]]) 
1 
2 
3 
1 
2 
[[10] 2] 
[[10] 2 3 [[10] 2]] 
1 
2 
[[10] 2] 
[[[10] 2 3 [[10] 2]] [[10] 2]] 
[[[10] 2 3 [[10] 2]] [[10] 2]] 

user> 
(def query [:db/id 
      :list/title 
      :list/type 
      {:list/items [:db/id 
          :list/title 
          :list/type 
          {:list/items []}]}]) 
#'user/query 

user> (clojure.walk/postwalk #(if (= :list/items %) 
           '(default :list/items []) 
           %) 
          query) 
[:db/id :list/title :list/type 
{(default :list/items []) [:db/id :list/title :list/type {(default :list/items []) []}]}] 

postwalkは葉が新しいコレクションに置き換えられている場合でも、内容に深く行きませんところで、あなたの大文字小文字の区別のために、prewalk-replace/postwalk-replaceという素晴らしい機能があります。

user> (clojure.walk/postwalk-replace 
     {:list/items '(default :list/items [])} query) 

[:db/id :list/title :list/type 
{(default :list/items []) [:db/id :list/title :list/type {(default :list/items []) []}]}] 

更新後、コメントの後に: いくつかの(合成)いくつかの代替の制御の例。あなたは、ネストされたベクトルのいくつかの任意のコレクション内の特定のアイテムを交換するために、一度だけ(あなたはそれを参照してください1回目)のアイテムを交換したいとしましょう、と変わらず、残りを残す:

user> (require '[clojure.zip :as z]) 

user> 
(defn replace-once [rep coll] 
    (loop [curr (z/vector-zip coll) rep rep] 
    (if (empty? rep) (z/root curr) 
     (let [n (z/node curr) r (rep n)] 
      (cond (z/end? curr) (z/root curr) 
       r (recur (z/replace curr r) (dissoc rep n)) 
       :else (recur (z/next curr) rep)))))) 
#'user/replace-once 

user> (replace-once {1 100 2 200} [[4 3 2] [10 1 2] 1 2 [5 3 2]]) 
[[4 3 200] [10 100 2] 1 2 [5 3 2]] 

(ここであなただけの置き換え削除置換候補からのアイテムマップ(rep)、それが空になるまで再帰でさらに渡します)

+0

ああ、postwalk-replaceは完璧です。好奇心から、プレウォークを使用してクエリを操作する必要があるとしましょう。置換を追跡するためにアトムを使用する必要がありますか?それとも別の方法がありますか? – egracer

+0

どういう意味ですか?ちょうど今それを考えることができません) – leetwinski

+1

しかし、私はあなたのためにそれのための 'ジッパー 'を見てお勧めします。ツリー上での置換と反復の細かい制御が必要な場合。例えば、いくつかのアイテムを置き換えて、このアイテムをスキップしたり、いくつかのアキュムレータでそれを追跡したりすることができます(ジッパーは 'loop/recur'でうまく動作するので – leetwinski