2016-03-27 9 views
1

Ich habe über dieses Problem zu denken, aber ich kann nicht die Schritte herauszufinden, meine Funktion zu bauen:Rekursion in html wie Datenstruktur clojure

ich einen Schluckauf wie HTML-Daten als Eingabe habe, ist diese Struktur komponiert von hTML und benutzerdefinierten Elementen, zB:

Format: [tag-name Optionen & Körper]

[:a {} []] ;; simple 
[:a {} [[:span {} []]]] ;; nested component 
[:other {} []] ;; custom component at tag-name 
[:a {} [[:other {} []]]] ;; custom component at body 

Jedes Mal, th E-Struktur ein benutzerdefiniertes Element hat, soll ich es Darstellung durch die HTML-render (ersetzen), die in den database ist, kann das individuelle Element oder Körper bei Tag-Namen vorhanden sein:

(def example 
    [:div {} [[:a {} []] 
      [:custom {} []]]]) 

    (def database { 
     :custom [[:a {} [] 
       [:div {} []]}) 

(def expected-result 
    [:div {} [[:a {} []] 
      [:a {} []] 
      [:div {} []]]]) 

Das Problem war: Wie erstelle eine Funktion, die diese Daten nimmt, suche nach dem Tag und dem Körper der Komponente, wenn es ein benutzerdefiniertes Element durch das Element database ersetzt, schaue es nach dem Ersetzen erneut an, wenn es neue Komponenten gibt, die diese Schritte ausführen wieder ...

Ich habe bereits eine Funktion (custom-compon ent?), die einen Tag-Namen und gibt einen booleschen nimmt, wenn ein Element Brauch ist:

(custom-component? :a) ;; false 
(custom-component? :test) ;; true 

Vielen Dank für jede Hilfe, ich bin wirklich auf diese stecken.

+0

Haben Sie das [Reagent-Projekt] (https://reagent-project.github.io/) ausgecheckt? – jmargolisvt

+0

@jmargolisvt Ich brauche Ausgabe nur HTML, am Ende wird alles statische HTML sein (das ist das Ziel) –

Antwort

4

clojure hat eine besondere Möglichkeit, diese Aufgabe von fullfilling - Reißverschluss: http://josf.info/blog/2014/03/28/clojure-zippers-structure-editing-with-your-mind/

hier ein lückenhaftes Beispiel für Ihre Frage Lösung ist (ich habe eine weitere Komponente in Ihre database, füge auch rekursiv geschieht zu zeigen, dass ersetzen in eine neu hinzugefügte Komponente):

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

(def example 
    [:div {} [[:custom2 {} []] 
      [:a {} []] 
      [:custom {} []]]]) 

(def database {:custom [[:a {} []] 
         [:div {} [[:custom2 {} [[:p {} []]]]]]] 
       :custom2 [[:span {} [[:form {} []]]]]}) 

(defn replace-tags [html replaces] 
    (loop [current (z/zipper 
        identity last 
        (fn [node items] 
        [(first node) (second node) (vec items)]) 
        html)] 
    (if (z/end? current) 
     (z/root current) 
     (if-let [r (-> current z/node first replaces)] 
     (recur (z/remove (reduce z/insert-right current (reverse r)))) 
     (recur (z/next current)))))) 

in repl:

user> (replace-tags example database) 
[:div {} [[:span {} [[:form {} []]]] 
      [:a {} []] 
      [:a {} []] 
      [:div {} [[:span {} [[:form {} []]]]]]]] 

aber Vorsicht: Es ist nicht Zyklen in Ihrem Ersatz nicht berechnen, wenn Sie also eine zirkuläre Abhängigkeit wie diese:

(def database {:custom [[:a {} []] 
         [:div {} [[:custom2 {} [[:p {} []]]]]]] 
       :custom2 [[:span {} [[:custom {} []]]]]}) 

es würde eine Endlosschleife erzeugen.

+2

Große Antwort! Es führte mich dazu, mein Wissen über Zipper zu aktualisieren :) Eine Frage allerdings: Da die Eingabe Hiccup-Markup ist, könnte es Kinder im Element-Vektor "entpackt" haben (z.B. '[: div" Child1 "" Child2 "]'). Sollte nicht die Funktion 'branch?' Anstelle von 'identity'' 'vector?' 'Und 'children' Funktion anstelle' '' 'sein?' (Fn [[tag & xs]] (if (map? (Erste xs)) (nächste xs) xs)) '? –

+0

Nun, wahrscheinlich sollte es so sein. Meine Antwort bietet nur eine Lösung für die "Schluckauf-ähnliche" Struktur aus der Frage des Ops, nicht die ganze gültige Schluckaufsyntax. Natürlich muss man alle Varianten für eine produktionsfertige Lösung sorgfältig durchdenken. – leetwinski

+0

Danke für die Antwort, ich lese immer noch über Reißverschlüsse. –