2012-07-10 4 views
5

Ich arbeite in Clojure mit einer Java-Klasse, die eine Retrieval-API für eine domänenspezifische Binärdatei mit einer Reihe von Datensätzen bietet.Interop von clojure a mit nicht-standard iterativen Java API

Die Java-Klasse wird mit einer Datei initialisiert und stellt dann eine .query-Methode bereit, die eine Instanz einer inneren Klasse zurückgibt, die nur eine Methode .next hat und daher nicht gut mit der üblichen Java-Auflistungs-API zusammenspielt. Weder die äußere noch die innere Klasse implementiert irgendeine Schnittstelle.

Die .query-Methode kann null anstelle der inneren Klasse zurückgeben. Die Methode .next gibt eine Datensatzzeichenfolge oder null zurück, wenn keine weiteren Datensätze gefunden werden. Sie kann sofort nach dem ersten Aufruf null zurückgeben.

Wie mache ich diese Java-API gut aus clojure, ohne weitere Java-Klassen zu schreiben?

Das Beste, was ich tun konnte, ist:


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

Das gibt mir einen Vektor der Ergebnisse mit der clojure ff Abstraktionen zu arbeiten. Gibt es andere Möglichkeiten, einen Seq von der Java-API zu offenbaren, entweder mit Lazy-Seq oder mit Protokollen?

+0

Dies ist eine interessante Frage, aber was meinen Sie mit einer nicht standardmäßigen Java API? – octopusgrabbus

+0

@octopusgrabbus speziell ich meine, der Java-Code bietet keinen java.util.Iterator noch java.lang.Iterable zu implementieren. Logischerweise bietet der Java-Code eine Iteration, jedoch ohne eine Verbindung zur Standard-API. –

Antwort

4

Ohne fiel auf lazy-seq:

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

Statt (complement nil?) Sie auch nur identity verwenden könnte, wenn .next nicht boolean false zurück.

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

Ich würde auch ein wenig die Einstiegspunkte umstrukturieren.

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

nette Lösungen! – Gert

4

Ihr Code ist nicht faul wie es wäre, wenn Sie Iterable verwenden würden, aber Sie können die Lücke mit Lazy-Seq wie folgt füllen.

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

Vielleicht sollten Sie die Abfrage-Methode umschließen, um sich auch vor dem ersten Nullwert zu schützen.

+2

Bewegen Sie den 'Lazy-Seq' um das' When-Let' herum. –

4

Scheint wie eine gute Passform für 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")) 
Verwandte Themen