html 中的递归,例如使用 clojure 的数据结构
Recursion in html like data structure using clojure
我一直在思考这个问题,但我想不出构建我的函数的步骤:
我有一个打嗝像html数据作为输入,这个结构由html和自定义元素组成,示例:
格式:[标签名称 选项 & 正文]
[:a {} []] ;; simple
[:a {} [[:span {} []]]] ;; nested component
[:other {} []] ;; custom component at tag-name
[:a {} [[:other {} []]]] ;; custom component at body
每次结构有自定义元素时,我应该用 database
中的 html 表示来渲染(替换)它,自定义元素可能出现在 tag-name 或 body:
(def example
[:div {} [[:a {} []]
[:custom {} []]]])
(def database {
:custom [[:a {} []
[:div {} []]})
(def expected-result
[:div {} [[:a {} []]
[:a {} []]
[:div {} []]]])
问题是:如何创建一个获取此数据的函数,查找组件的标签和主体,如果有自定义元素将其替换为 database
元素,替换后,查看再来一遍,如果有新组件再做一遍这个步骤...
我已经有一个函数(custom-component?),它接受一个标签名称和returns一个布尔值,如果是一个自定义元素:
(custom-component? :a) ;; false
(custom-component? :test) ;; true
感谢您的帮助,我真的卡在这上面了。
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))))))
回复:
user> (replace-tags example database)
[:div {} [[:span {} [[:form {} []]]]
[:a {} []]
[:a {} []]
[:div {} [[:span {} [[:form {} []]]]]]]]
但请注意:它不会计算替换项内的循环,因此如果您有这样的循环依赖项:
(def database {:custom [[:a {} []]
[:div {} [[:custom2 {} [[:p {} []]]]]]]
:custom2 [[:span {} [[:custom {} []]]]]})
它会产生一个无限循环。
我一直在思考这个问题,但我想不出构建我的函数的步骤:
我有一个打嗝像html数据作为输入,这个结构由html和自定义元素组成,示例:
格式:[标签名称 选项 & 正文]
[:a {} []] ;; simple
[:a {} [[:span {} []]]] ;; nested component
[:other {} []] ;; custom component at tag-name
[:a {} [[:other {} []]]] ;; custom component at body
每次结构有自定义元素时,我应该用 database
中的 html 表示来渲染(替换)它,自定义元素可能出现在 tag-name 或 body:
(def example
[:div {} [[:a {} []]
[:custom {} []]]])
(def database {
:custom [[:a {} []
[:div {} []]})
(def expected-result
[:div {} [[:a {} []]
[:a {} []]
[:div {} []]]])
问题是:如何创建一个获取此数据的函数,查找组件的标签和主体,如果有自定义元素将其替换为 database
元素,替换后,查看再来一遍,如果有新组件再做一遍这个步骤...
我已经有一个函数(custom-component?),它接受一个标签名称和returns一个布尔值,如果是一个自定义元素:
(custom-component? :a) ;; false
(custom-component? :test) ;; true
感谢您的帮助,我真的卡在这上面了。
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))))))
回复:
user> (replace-tags example database)
[:div {} [[:span {} [[:form {} []]]]
[:a {} []]
[:a {} []]
[:div {} [[:span {} [[:form {} []]]]]]]]
但请注意:它不会计算替换项内的循环,因此如果您有这样的循环依赖项:
(def database {:custom [[:a {} []]
[:div {} [[:custom2 {} [[:p {} []]]]]]]
:custom2 [[:span {} [[:custom {} []]]]]})
它会产生一个无限循环。