2012-07-10 8 views
5

Estoy trabajando en clojure con una clase java que proporciona una API de recuperación para un archivo binario específico de dominio que contiene una serie de registros.Interoperación de clojure a con API iterativa no estándar API

La clase java se inicializa con un archivo y luego proporciona un método .query que devuelve una instancia de una clase interna que tiene solo un método .next, por lo que no funciona bien con la API java collections habitual. Ni la clase externa ni la interna implementan ninguna interfaz. El método .query puede devolver nulo en lugar de la clase interna. El método .next devuelve una cadena de registro o nulo si no se encuentran registros adicionales, puede devolver nulo inmediatamente después de la primera llamada.

¿Cómo hago para que esta API de Java funcione bien desde clojure sin escribir más clases de Java?

Lo mejor que podía llegar a decir:


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

Esto me da un vector de los resultados de trabajar con las abstracciones clojure seq. ¿Hay otras maneras de exponer un seq de la API de Java, ya sea con lazy-seq o usando protocolos?

+0

Esta es una pregunta interesante, pero ¿qué quiere decir con una API Java no estándar? – octopusgrabbus

+0

@octopusgrabbus específicamente me refiero a que el código de Java no proporciona un java.util.Iterator ni implementa java.lang.Iterable. Lógicamente, el código java proporciona iteración pero no tiene conexión con la API estándar para hacerlo. –

Respuesta

4

Sin caer a lazy-seq:

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

En lugar de (complement nil?) usted podría también utilizar sólo si identity.next no devuelve booleano false.

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

También reestructuraría un poco los puntos de entrada.

(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

buenas soluciones! – Gert

4

Su código no es flojo como lo sería si estuviera utilizando Iterable, pero puede llenar el vacío con lazy-seq de la siguiente manera.

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

Quizás deba ajustar el método de búsqueda para protegerse también del primer valor nulo.

+2

Mueva 'lazy-seq' alrededor del' when-let'. –

4

parece un buen ajuste para 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"))