2012-07-10 67 views
5

我正在clojure中使用java類,它爲包含一系列記錄的域特定二進制文件提供檢索API。clojure a與非標準迭代java API的互操作性

java類初始化爲一個文件,然後提供一個.query方法,該方法返回一個只有一個方法.next的內部類的實例,因此不能很好地與通常的Java集合API一起玩。外層和內層類都不實現任何接口。

.query方法可能會返回null而不是內部類。 .next方法返回一個記錄字符串,如果沒有找到其他記錄,則返回null,它可能在第一次調用時立即返回null。

如何讓這個java API在clojure中很好的工作而無需編寫更多的java類?

我能想出的最好的是:


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

這給我結果的矢量與Clojure的序列抽象的工作。有沒有其他的方法可以通過lazy-seq或使用協議來暴露java API中的seq?

+0

這是一個有趣的問題,但是您是指非標準Java API的含義? – octopusgrabbus 2012-07-10 15:06:23

+0

@octopusgrabbus具體而言,我的意思是Java代碼不提供java.util.Iterator,也不實現java.lang.Iterable。邏輯上,Java代碼提供迭代,但沒有與標準API的連接。 – 2012-07-10 15:14:55

回答

4

沒有下降到lazy-seq

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

相反(complement nil?),你也可以只使用identity如果.next不返回布爾false

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

我也會重組一點點的入口點。

(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

不錯的解決方案! – Gert 2012-07-11 10:56:37

4

您的代碼並不像使用Iterable那樣懶,但您可以使用lazy-seq填充空格,如下所示。

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

也許你會包裹查詢方法以保護你自己的第一個null值。

+2

將「lazy-seq」移到'when-let'周圍。 – 2012-07-10 19:19:25

4

似乎是一個非常適合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"))