使用此数据
(def html
'(html (head (title "the fortune cookie institute"))
(body
(h1 "Welcome to the Fortune Cookie Institute")
(p "Our fortunes are guaranteed accurate no matter what.")
(br)
(div (p "Like these"))
(ol
(li "You will gain weight")
(li "Taxes will rise")
(li "Fusion power will always be 50 years away"))
(br)
(p "Submit your own fortunes to [email protected]!"))))
选项 1:使用内置的clojure walk
(require '[clojure.walk :refer [prewalk]])
(prewalk (fn [x] (if (= x 'p) 'h2 x)) html)
请注意,walker会继续遍历,并且会在列表头部以外的位置找到'p(不希望这样做)。
选项 2:使用specter
(require '[com.rpl.specter :refer [ALL FIRST setval recursive-path]] )
(setval [ALL (recursive-path [] RECURSE
(cond-path
[sequential? FIRST (pred= 'p)] FIRST
sequential? [ALL RECURSE]))]
'h2
html)
这里的 Specter 只查找列表头部的 'p(使用 sequential? 而非 list? 作为你的结构非常接近 Hiccup,Hiccup 会使用向量。Sequential 是用于列表和向量的工作方式。
option3
(defn shout [html]
(if-not (sequential? html)
html
(if (= 'p (first html))
(cons 'h2 (->> (rest html)
(map shout)))
(map shout html))))
我认为它真正递归和消耗了堆栈,但由于 HTML 的深度并不会造成影响。我不确定是否存在一个 loop [h html] ... (recur ...
的解决方案更优。