How do turn a java Iterator-like object into a clojure sequence
Asked Answered
D

4

13

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

Dewar answered 10/2, 2012 at 10:0 Comment(0)
D
10

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)))))))
Dewar answered 10/2, 2012 at 18:2 Comment(1)
This (reifying Iterable instead of Iterator) is one approach; another is to reify Iterator and then wrap it with iterator-seq.Same
P
7

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.]

Preengage answered 10/2, 2012 at 10:54 Comment(2)
Thanks. Not quite, but it pointed me into the right direction and helped me learn about practical use of reify.Dewar
I already got a tested correct version, just can't self-answer yet due to some reputation restriction. :)Dewar
E
4

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?

Eugeniaeugenics answered 7/7, 2013 at 19:54 Comment(1)
This does not work as iterator-seq explicitly only works with java.util.Iterator, whereas the object described here is an info.aduna.iteration.Iteration. Using iterator-seq leads to a ClassCastException.Nez
W
2

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.

Wilt answered 26/3, 2013 at 0:33 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.