문제

I am working in clojure with a java class which provides a retrieval API for a domain specific binary file holding a series of records.

The java class is initialized with a file and then provides a .query method which returns an instance of an inner class which has only one method .next, thus not playing nicely with the usual java collections API. Neither the outer nor inner class implements any interface.

The .query method may return null instead of the inner class. The .next method returns a record string or null if no further records are found, it may return null immediately upon the first call.

How do I make this java API work well from within clojure without writing further java classes?

The best I could come up with is:


(defn get-records
  [file query-params]
  (let [tr (JavaCustomFileReader. file)]
    (if-let [inner-iter (.query tr query-params)] ; .query may return null                                                               
      (loop [it inner-iter
             results []]
       (if-let [record (.next it)]
         (recur it (conj results record))
         results))
      [])))

This gives me a vector of results to work with the clojure seq abstractions. Are there other ways to expose a seq from the java API, either with lazy-seq or using protocols?

도움이 되었습니까?

해결책

Without dropping to lazy-seq:

(defn record-seq
  [q]
  (take-while (complement nil?) (repeatedly #(.next q))))

Instead of (complement nil?) you could also just use identity if .next does not return boolean false.

(defn record-seq
  [q]
  (take-while identity (repeatedly #(.next q))))

I would also restructure a little bit the entry points.

(defn query
  [rdr params]
  (when-let [q (.query rdr params)]
    (record-seq q)))

(defn query-file
  [file params]
  (with-open [rdr (JavaCustomFileReader. file)]
    (doall (query rdr params))))

다른 팁

Seems like a good fit for lazy-seq:

(defn query [file query]
  (.query (JavaCustomFileReader. file) query))

(defn record-seq [query]
  (when query
    (when-let [v (.next query)]
      (cons v (lazy-seq (record-seq query))))))

;; usage:
(record-seq (query "filename" "query params"))

Your code is not lazy as it would be if you were using Iterable but you can fill the gap with lazy-seq as follows.

(defn query-seq [q]
  (lazy-seq
    (when-let [val (.next q)]
      (cons val (query-seq q)))))

Maybe you shoul wrap the query method to protect yourself from the first null value as well.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top