我使用Sesame库在内存中的三元组存储上运行SPARQL查询。
我正在使用Clojure来实现这一点。
查询结果是一个类似于[1]对象的自定义Iterator,因此clojureseq不能开箱即用。
将自定义的类似java Iterator的对象转换为clojure序列的最优雅的方法是什么?
我想到的最明显也是最愚蠢的想法是循环它并建立一个clojure向量,但我相信有更优雅的方法来解决这个问题。
[1]http://www.openrdf.org/doc/sesame2/api/info/aduna/iteration/Iteration.html
测试版本:
(defn iteration->seq [iteration]
(seq
(reify java.lang.Iterable
(iterator [this]
(reify java.util.Iterator
(hasNext [this] (.hasNext iteration))
(next [this] (.next iteration))
(remove [this] (.remove iteration)))))))
将类似迭代器的对象封装在实际实现Iterator
接口的对象中如何?类似以下内容(未测试):
(defn iteration-seq [iteration]
(iterator-seq
(reify java.util.Iterator
(hasNext [this] (.hasNext iteration))
(next [this] (.next iteration))
(remove [this] (.remove iteration)))))
据我所知,Iteration
接口相对于标准Iterator
接口的唯一优势(如果你想这么称呼它的话)是它允许声明已检查的异常,而这些异常无论如何都不会在Clojure中使用。
[更新:如@amaloy在另一个答案的评论中所建议的,将代码更正为使用iterator-seq
而不是seq
。]
为什么不试试clojure.core/iterator-seq?
user=> (doc iterator-seq)
-------------------------
clojure.core/iterator-seq
([iter])
Returns a seq on a java.util.Iterator. Note that most collections
providing iterators implement Iterable and thus support seq directly.
正如您在上面的文档字符串中看到的,您甚至可能不需要显式使用iterator-seq
。你刚才有没有试着把Java迭代器当作REPL中的一个序列?
java iterable和Iterator 的纯函数可迭代到惰性序列代码
(defn iter-seq
([iterable]
(iter-seq iterable (.iterator iterable)))
([iterable i]
(lazy-seq
(when (.hasNext i)
(cons (.next i) (iter-seq iterable i))))))
对于自定义迭代器,请替换.iterator、.hasNext和.next调用。
优点是它是纯函数的,因为它将可迭代性作为一个参数。其他发布的解决方案采用可变的迭代器参数,因此函数可能会根据内部迭代器状态返回不同的序列,这违反了引用透明度。该功能也因其懒惰而张扬。