我有这段代码从XML构建句子如下所示。我想知道什么是替代代码,在被黑客攻击后会更具可读性。
(mapcat
(fn [el]
(map special-join
(map
(fn [el] (map zip-xml/text (zip-xml/xml-> el :word)))
(zip-xml/xml-> el :sentence))))
(zip-xml/xml-> root :document))
上面的代码不是很易读,因为重复的内联函数定义与嵌套探测相结合,但是将它们拆分成独立的函数,就像在this official tutorial中一样,对于我这样简单而言对我来说真的没有意义例。
为了完整性,这里是解析
的重复XML结构<document>
<sentence id="1">
<word id="1.1">Foo</w>
<word id="1.2">bar</w>
</sentence>
</document>
答案 0 :(得分:3)
拉链在这种情况下可能有点过分。 clojure.xml/parse
将为您提供一个表示HTML的简单数据结构。
(require '[clojure.xml :as xml] '[clojure.string :as string])
(def doc
(->
"<document>
<sentence id=\"1\">
<word id=\"1.1\">
Foo
</word>
<word id=\"1.2\">
bar
</word>
</sentence>
</document>
" .getBytes java.io.ByteArrayInputStream. xml/parse))
然后,您可以使用xml-seq
获取所有<sentence>
代码及其子代,收集孩子的文字内容,修剪空白以及加入空格。
(->> doc
xml-seq
(filter (comp #{:sentence} :tag))
(map :content)
(map #(transduce
(comp
(mapcat :content)
(map string/trim)
(interpose " "))
str %)))
答案 1 :(得分:0)
我不喜欢拉链在Clojure中的工作方式,我没有看过clojure.zip/xml-zip
或clojure.data.zip/xml->
(令人困惑的是它们是两个独立的库!)。
相反,我可以建议你试试tupelo.forest
图书馆吗?这是an overview from the 2017 Clojure/Conj。
以下是使用tupelo.forest
的实时解决方案。我添加了第二句话使其更有趣:
(dotest
(with-forest (new-forest)
(let [xml-str (ts/quotes->double
"<document>
<sentence id='1'>
<word id='1.1'>foo</word>
<word id='1.2'>bar</word>
</sentence>
<sentence id='2'>
<word id='2.1'>beyond</word>
<word id='2.2'>all</word>
<word id='2.3'>recognition</word>
</sentence>
</document>")
root-hid (add-tree-xml xml-str)
>> (remove-whitespace-leaves)
bush-no-blanks (hid->bush root-hid)
sentence-hids (find-hids root-hid [:document :sentence])
sentences (forv [sentence-hid sentence-hids]
(let [word-hids (hid->kids sentence-hid)
words (mapv #(grab :value (hid->leaf %)) word-hids)
sentence-text (str/join \space words)]
sentence-text))
]
(is= bush-no-blanks
[{:tag :document}
[{:id "1", :tag :sentence}
[{:id "1.1", :tag :word, :value "foo"}]
[{:id "1.2", :tag :word, :value "bar"}]]
[{:id "2", :tag :sentence}
[{:id "2.1", :tag :word, :value "beyond"}]
[{:id "2.2", :tag :word, :value "all"}]
[{:id "2.3", :tag :word, :value "recognition"}]]])
(is= sentences
["foo bar"
"beyond all recognition"]))))
想法是为每个句子找到hid
(十六进制ID,如指针)。在forv
循环中,我们找到每个句子的子节点,提取:value
,并将其联合成一个字符串。单元测试显示从XML(删除空白节点后)和最终结果解析的树结构。请注意,我们忽略id
字段并仅使用树结构来理解句子。
tupelo.forest
的文档仍在进行中,但您可以see many live examples here。
图珀洛项目lives on GitHub。\
我一直在考虑流数据问题,并添加了一个新函数proc-tree-enlive-lazy
来启用大型数据集的延迟处理。这是一个例子:
(let [xml-str (ts/quotes->double
"<document>
<sentence id='1'>
<word id='1.1'>foo</word>
<word id='1.2'>bar</word>
</sentence>
<sentence id='2'>
<word id='2.1'>beyond</word>
<word id='2.2'>all</word>
<word id='2.3'>recognition</word>
</sentence>
</document>")
(let [enlive-tree-lazy (clojure.data.xml/parse (StringReader. xml-str))
doc-sentence-handler (fn [root-hid]
(remove-whitespace-leaves)
(let [sentence-hid (only (find-hids root-hid [:document :sentence]))
word-hids (hid->kids sentence-hid)
words (mapv #(grab :value (hid->leaf %)) word-hids)
sentence-text (str/join \space words)]
sentence-text))
result-sentences (proc-tree-enlive-lazy enlive-tree-lazy
[:document :sentence] doc-sentence-handler)]
(is= result-sentences ["foo bar" "beyond all recognition"])) ))
这个想法是你处理连续的子树,在这种情况下,只要你得到[:document :sentence]
的子树路径。您传入处理程序函数,该函数将接收root-hid
的{{1}}。然后将处理程序的返回值放在返回给调用者的输出延迟序列上。