2016-03-27 1 views
1

Я думал об этой проблеме, но я не могу понять, шаги, чтобы построить свою функцию:Рекурсии в HTML, как структуры данных, используя Clojure

У меня есть икота, как HTML данные в качестве входных данных, эта структура состоит из HTML и пользовательских элементов, например:

формат: [тег имя опции & тело]

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

Каждый раз, когда й е структуры есть пользовательский элемент, я должен вынести (заменить) его HTML-представление, которое находится в database, пользовательский элемент может присутствовать в тегах имени- или тела:

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

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

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

Проблема было: Как создать функцию, которая берет эти данные, искать тег и тело компонента, если есть элемент, заменяющий его элементом database, после его замены просмотрите его снова, если есть новые компоненты, выполните следующие действия снова ...

У меня уже есть функция (custom-component лор?), который принимает имя тега и возвращает логическое значение, если это пользовательский элемент:

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

Спасибо за любую помощь, я действительно застрял на этом.

+0

Вы проверили [Проект реагента] (https://reagent-project.github.io/)? – jmargolisvt

+0

@jmargolisvt Мне нужен вывод только html, в конце будет весь статический html (это цель) –

ответ

4

Clojure имеет особый способ fullfilling этой задачи - застежки-молнии: http://josf.info/blog/2014/03/28/clojure-zippers-structure-editing-with-your-mind/

здесь схематичный пример решения Вашего вопроса (в я добавил еще один компонент в вашем database, чтобы показать, что заменить также происходит рекурсивно в недавно добавленного компонента):

(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)))))) 

в REPL:

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

, но будьте осторожны: он не вычисляет циклы внутри вашей замены, так что если у вас есть циклическая зависимость, как это:

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

он будет производить бесконечный цикл.

+2

Отличный ответ! Это привело меня к освещению моих знаний о молниях :) Один вопрос: в качестве входных данных используется разметка Hiccup, у него могут быть дети «распакованы» в векторе элементов (например, «[: div« Child1 »« Child2 »]'). Если функция 'branch?' Вместо 'identity' не является' vector? 'И' children', а не 'last' be' (fn [[tag & xs]] (if (map? (First xs)) (следующий xs) xs)) '? –

+0

Ну, наверное, так должно быть. Мой ответ только предоставляет решение для структуры «hiccup like» из вопроса op, а не для всего действительного синтаксиса hiccup. Очевидно, нужно тщательно продумать все варианты готового решения для производства. – leetwinski

+0

Спасибо за ответ, я все еще читаю про молнии. –