我正在尝试处理新的defprotocol
,reify
等。
我有一个来自XPath通话的org.w3c.dom.NodeList
,我想"转换"它是一个ISeq。
在Scala中,我实现了一种隐式转换方法:
implicit def nodeList2Traversable(nodeList: NodeList): Traversable[Node] = {
new Traversable[Node] {
def foreach[A](process: (Node) => A) {
for (index <- 0 until nodeList.getLength) {
process(nodeList.item(index))
}
}
}
}
NodeList
包含方法int getLength()
和Node item(int index)
。
如何在Clojure中完成等效操作?我希望我需要使用defprotocol
。我需要定义哪些函数来创建seq
?
如果我使用loop
和recur
进行简单,天真的转换,我最终会得到一个非惰性结构。
答案 0 :(得分:7)
大多数Clojure的序列处理函数返回惰性序列,包括map
和range
函数:
(defn node-list-seq [^org.w3c.dom.NodeList node-list]
(map (fn [index] (.item node-list index))
(range (.getLength node-list))))
请注意,上面的NodeList类型提示不是必需的,但可以提高性能。
现在您可以像这样使用该功能:
(map #(.getLocalName %) (node-list-seq your-node-list))
答案 1 :(得分:6)
使用for comprehension,这些会产生延迟序列。
这是给你的代码。我花时间在命令行上运行它;您只需要替换已解析的XML文件的名称。
警告1:避免定义您的变量。改为使用局部变量。
警告2:这是XML的Java API,因此对象是可变的;因为你有一个懒惰的序列,如果你在迭代时对可变DOM树发生任何变化,你可能会有令人不快的种族变化。
警告3:即使这是一个懒惰的结构,整个DOM树已经在内存中了(但我不确定这最后的评论。我认为API尝试推迟在内存中读取树,直到需要,但是,没有保证)。因此,如果您遇到大型XML文档的问题,请尝试避免使用DOM方法。
(require ['clojure.java.io :as 'io])
(import [javax.xml.parsers DocumentBuilderFactory])
(import [org.xml.sax InputSource])
(def dbf (DocumentBuilderFactory/newInstance))
(doto dbf
(.setValidating false)
(.setNamespaceAware true)
(.setIgnoringElementContentWhitespace true))
(def builder (.newDocumentBuilder dbf))
(def doc (.parse builder (InputSource. (io/reader "C:/workspace/myproject/pom.xml"))))
(defn lazy-child-list [element]
(let [nodelist (.getChildNodes element)
len (.getLength nodelist)]
(for [i (range len)]
(.item nodelist i))))
;; To print the children of an element
(-> doc
(.getDocumentElement)
(lazy-child-list)
(println))
;; Prints clojure.lang.LazySeq
(-> doc
(.getDocumentElement)
(lazy-child-list)
(class)
(println))