我一直在考虑这个问题,但我无法弄清楚构建我的函数的步骤:
我有像html数据一样的打嗝作为输入,这个结构由html和自定义元素组成,例如:
格式:[标记名称选项&的体
[:a {} []] ;; simple
[:a {} [[:span {} []]]] ;; nested component
[:other {} []] ;; custom component at tag-name
[:a {} [[:other {} []]]] ;; custom component at body
每次结构都有自定义元素时,我应该通过database
中的html表示来渲染(替换),自定义元素可能出现在标记名称或正文:
(def example
[:div {} [[:a {} []]
[:custom {} []]]])
(def database {
:custom [[:a {} []
[:div {} []]})
(def expected-result
[:div {} [[:a {} []]
[:a {} []]
[:div {} []]]])
问题是:如何创建一个获取此数据的函数,查找组件的标记和正文,如果有一个自定义元素将其替换为database
元素,则在替换它之后,查看它再次,如果有新组件再次执行此步骤......
我已经有一个函数(自定义组件?),它接受一个标记名称并返回一个布尔值if if是一个自定义元素:
(custom-component? :a) ;; false
(custom-component? :test) ;; true
感谢您的帮助,我真的坚持这一点。
答案 0 :(得分:4)
clojure有一种特殊的方式来完成这项任务 - 拉链: 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 {} []]]]]})
它会产生无限循环。