Как превратить java-итератор-подобный объект в последовательность clojure
Я использую библиотеку Sesame для запуска запросов SPARQL по трем хранилищу в памяти.
Я использую Clojure для достижения этого.
Результат запроса является обычным Iterator-подобным [1] объектом, поэтому Clojure seq не работает на нем из коробки.
Каков самый элегантный способ превратить пользовательский Java-объект Iterator в последовательность Clojure?
Наиболее очевидной и немой идеей, которая мне пришла в голову, является ее надстройка и создание вектора Clojure, но я уверен, что есть более элегантный подход к этой проблеме.
[1] http://www.openrdf.org/doc/sesame2/api/info/aduna/iteration/Iteration.html
Ответы
Ответ 1
Тестируемая версия:
(defn iteration->seq [iteration]
(seq
(reify java.lang.Iterable
(iterator [this]
(reify java.util.Iterator
(hasNext [this] (.hasNext iteration))
(next [this] (.next iteration))
(remove [this] (.remove iteration)))))))
Ответ 2
Как обернуть объект, похожий на итератор, в объект, который фактически реализует интерфейс Iterator
? Что-то вроде следующего (не проверено):
(defn iteration-seq [iteration]
(iterator-seq
(reify java.util.Iterator
(hasNext [this] (.hasNext iteration))
(next [this] (.next iteration))
(remove [this] (.remove iteration)))))
Насколько я могу судить, единственным преимуществом (если вы хотите это назвать) интерфейса Iteration
над стандартным интерфейсом Iterator
является то, что он позволяет объявлять проверенные исключения, которые не используются в Clojure в любом случае.
[Обновить: исправленный код для использования iterator-seq
вместо seq
, как это было предложено @amalloy в комментарии к другому ответу.]
Ответ 3
Чистый функциональный код с итерируемой до ленивой последовательности для java Iterable и Iterator
(defn iter-seq
([iterable]
(iter-seq iterable (.iterator iterable)))
([iterable i]
(lazy-seq
(when (.hasNext i)
(cons (.next i) (iter-seq iterable i))))))
Для пользовательских итераторов замените .iterator,.hasNext и .next вызовы.
Преимущество состоит в том, что он чисто функциональный, поскольку он принимает итерабельность в качестве аргумента. Другие опубликованные решения принимают аргумент итератора, который изменен, поэтому функция может возвращать другую последовательность в зависимости от внутреннего состояния итератора, что нарушает ссылочную прозрачность.
Эта функция также ярка в отношении ее лени.
Ответ 4
Почему бы не дать clojure.core/iterator-seq попробовать?
user=> (doc iterator-seq)
-------------------------
clojure.core/iterator-seq
([iter])
Returns a seq on a java.util.Iterator. Note that most collections
providing iterators implement Iterable and thus support seq directly.
Как вы можете видеть в приведенной выше документации, вам может даже не понадобиться явно использовать iterator-seq
. Вы просто пытались обработать свой Java-итератор как последовательность в REPL?