I also posted this to StackOverflow, so sorry if you saw it there too. If 
you want some rep points over there you can answer there too (
http://stackoverflow.com/questions/12427518/clojure-lazy-seq-over-java-iterative-code
).

I'm trying to use create a Clojure seq from some iterative Java library 
code that I inherited. Basically what the Java code does is read records 
from a file using a parser, sends those records to a processor and returns 
an ArrayList of result. In Java this is done by calling parser.readData(), 
then parser.getRecord() to get a record then passing that record into 
processor.processRecord(). Each call to parser.readData() returns a single 
record or null if there are no more records. Pretty common pattern in Java.

So I created this next-record function in Clojure that will get the next 
record from a parser.

(defn next-record
  "Get the next record from the parser and process it."
  [parser processor]
  (let [datamap (.readData parser)
        row (.getRecord parser datamap)]
    (if (nil? row)
    nil
    (.processRecord processor row 100))))

The idea then is to call this function and accumulate the records into a 
Clojure seq (preferably a lazy seq). So here is my first attempt which 
works great as long as there aren't too many records:

(defn datamap-seq
  "Returns a lazy seq of the records using the given parser and processor"
  [parser processor]
  (lazy-seq
    (when-let [records (next-record parser processor)]
      (cons records (datamap-seq parser processor)))))

I can create a parser and processor, and do something like (take 5 
(datamap-seq parser processor)) which gives me a lazy seq. And as expected 
getting the (first) of that seq only realizes one element, doing count 
realizes all of them, etc. Just the behavior I would expect from a lazy seq.

Of course when there are a lot of records I end up with a 
StackOverflowException. So my next attempt was to use loop-recur to do the 
same thing.

(defn datamap-seq
  "Returns a lazy seq of the records using the given parser and processor"
  [parser processor]
  (lazy-seq
    (loop [records (seq '())]
      (if-let [record (next-record parser processor)]
        (recur (cons record records))
        records))))

Now using this the same way and defing it using (def results (datamap-seq 
parser processor)) gives me a lazy seq and doesn't realize any elements. 
However, as soon as I do anything else like (first results) it forces the 
realization of the entire seq.

Can anyone help me understand where I'm going wrong in the second function 
using loop-recur that causes it to realize the entire thing?

-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en

Reply via email to