Взаимодействие с clojure с нестандартным итеративным Java API

Я работаю в clojure с классом java, который предоставляет API поиска для двоичного файла, специфичного для домена, содержащего серию записей.

Класс java инициализируется файлом, а затем предоставляет метод .query, который возвращает экземпляр внутреннего класса, который имеет только один метод .next, что не очень хорошо сочетается с обычным API коллекций java. Ни внешний, ни внутренний класс не реализуют никакого интерфейса.

Метод .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 seq. Существуют ли другие способы предоставления последовательности из java API, либо с помощью отложенной последовательности, либо с использованием протоколов?


person Alex Stoddard    schedule 10.07.2012    source источник
comment
Это интересный вопрос, но что вы подразумеваете под нестандартным Java API?   -  person octopusgrabbus    schedule 10.07.2012
comment
В частности, @octopusgrabbus я имею в виду, что код Java не предоставляет java.util.Iterator и не реализует java.lang.Iterable . Логически код Java обеспечивает итерацию, но без подключения к стандартному API для этого.   -  person Alex Stoddard    schedule 10.07.2012


Ответы (3)


Не опускаясь до 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))))
person kotarak    schedule 11.07.2012

Похоже, он подходит для 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"))
person Gert    schedule 10.07.2012

Ваш код не ленив, как если бы вы использовали Iterable, но вы можете заполнить пробел с помощью lazy-seq следующим образом.

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

Возможно, вам следует обернуть метод запроса, чтобы защитить себя и от первого нулевого значения.

person sortega    schedule 10.07.2012
comment
Переместите lazy-seq вверх вокруг when-let. - person Alex Taggart; 10.07.2012