2016-05-13 5 views
0

ich brauche, um dynamisch Daten dieser Struktur zu ändern:Clojure Prewalk unendliche Rekursion für datomic Abfragen

[:db/id 
:list/title 
:list/type 
{:list/items [... lots of nested data ...]}] 

auf die folgenden:

[:db/id 
:list/title 
:list/type 
{(default :list/items []) [... lots of nested data ...]}] 

Da ich mehrere Anfragen Handhabung bin, kann ich sein sicher, dass der Join der vierte Punkt im Vektor sein wird. Aber ich muss jede Instanz von :list/items durch (default :list/items []) ersetzen.

Der einzige Weg, den ich kenne, ist mit clojure.walk/prewalk. Es führt jedoch zu unendlicher Rekursion:

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

Sobald die Wanderung findet :list/items und ersetzt sie durch '(default :list/items []), es dann die :list/items in dem ersetzten Wert findet, und ersetzt das. Und so weiter und so fort.

Ich kann ein Atom verwenden, um sicherzustellen, dass der Wert nur einmal ersetzt wird, aber das fühlt sich an wie Betrug.

Irgendwelche anderen Ansätze?

Antwort

1

in diesem Fall, dass Sie wahrscheinlich postwalk verwenden:

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 nicht auf den Inhalt noch tiefer geht, wenn das Blatt durch eine neue Kollektion ersetzt:

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]] 

durch die Art und Weise, gibt es eine nette Funktionen prewalk-replace/postwalk-replace für Ihren genauen Fall:

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 []) []}]}] 

Update, nach Kommentaren: einige (synthetische) Beispiel für mehr Kontrolle über den Ersatz. Angenommen, Sie haben bestimmte Elemente in einer beliebigen Sammlung von verschachtelten Vektoren, sondern ersetzen Sie das Element nur einmal (zum ersten Mal sehen Sie es), und den Rest unverändert ersetzt werden sollen:

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]] 

(hier einfach ersetzt entfernen Artikel aus Ersatzkandidaten Karte (rep), und übergeben Sie es weiter mit der Rekursion, bis es leer ist)

+0

Oh schön, postwalk-ersetzen ist perfekt. Aus Neugier, sagen wir mal, musste ich Prewalk verwenden, um die Abfrage auf andere Weise zu manipulieren. Müsste ich ein Atom verwenden, um den Ersatz zu verfolgen? Oder gibt es einen anderen Weg? – egracer

+0

was genau meinst du? (Ich kann jetzt nicht daran denken) – leetwinski

+1

aber ich würde dir vorschlagen, einen Blick auf 'Zipper' zu werfen. Wenn Sie eine feinkörnige Kontrolle über die Ersetzung und Iteration über den Baum wünschen. Zum Beispiel würde es Ihnen erlauben, einige Elemente zu ersetzen, und dann dieses Element zu überspringen, oder es in einem Akkumulator zu verfolgen (da Reißverschlüsse gut mit 'loop/recur 'funktionieren – leetwinski