Вопрос

I'm using the Sesame library to run SPARQL queries over an in-memory triple store.

I am using Clojure to achieve this.

A query result is a custom Iterator-like [1] object, so the clojure seq does not work on it out of the box.

What is the most elegant way to turn a custom java Iterator like object into a clojure sequence?

The most obvious and dumb idea that has come to my mind is to loop over it and build up a clojure vector, but I'm sure there is more elegant approach to this problem.

[1] http://www.openrdf.org/doc/sesame2/api/info/aduna/iteration/Iteration.html

Это было полезно?

Решение

The tested version:

(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)))))))

Другие советы

How about wrapping the iterator-like object in an object that actually implements the Iterator interface? Something like the following (not tested):

(defn iteration-seq [iteration]
  (iterator-seq
   (reify java.util.Iterator
     (hasNext [this] (.hasNext iteration))
     (next [this] (.next iteration))
     (remove [this] (.remove iteration)))))

As far as I can tell, the only advantage (if you want to call it that) of the Iteration interface over the standard Iterator interface is that it allows to declare checked exceptions, which are not used in Clojure anyways.

[Update: Corrected code to use iterator-seq instead of seq, as suggested by @amalloy in a comment on another answer.]

Pure functional iterable-to-lazy-sequence code for java Iterable and Iterator

(defn iter-seq
  ([iterable] 
    (iter-seq iterable (.iterator iterable)))
  ([iterable i] 
    (lazy-seq 
      (when (.hasNext i)
        (cons (.next i) (iter-seq iterable i))))))

For custom iterators replace .iterator, .hasNext and .next calls.

The advantage is that it's purely functional since it takes iterable as an argument. Other posted solutions take iterator argument which is mutable so the function may return a different sequence depending on internal iterator state what violates referential transparency. The function is also flamboyant about its laziness.

Why not give clojure.core/iterator-seq a try?

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.

As you can see in the docstring above, you might not even need to use iterator-seq explicitly. Did you just try treating your Java iterator as a sequence in the REPL?

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top