У меня есть lazy-seq, где каждый элемент требует времени для вычисления:
(defn gen-lazy-seq [size]
(for [i (range size)]
(do
(Thread/sleep 1000)
(rand-int 10))))
Можно ли поэтапно оценить эту последовательность и распечатать результаты. Когда я пытаюсь обработать его с помощью for
или doseq
, clojure всегда осознает всю ленивую последовательность перед выводом чего-либо:
(doseq [item (gen-lazy-seq 10)]
(println item))
(for [item (gen-lazy-seq 10)]
(println item))
Оба выражения будут ждать 10 секунд, прежде чем что-либо распечатать. Я рассматривал doall и dorun как решение, но они требуют, чтобы функция создания lazy-seq содержала println. Я хотел бы определить функцию создания lazy-seq и функцию печати lazy-seq отдельно и заставить их работать вместе, элемент за элементом.
Мотивация для попытки сделать это: у меня есть сообщения, приходящие по сети, и я хочу начать их обработку до того, как все будут получены. При этом все сообщения, соответствующие запросу, неплохо было бы сохранять в lazy-seq.
Изменить 1:
Ответ JohnJ показывает, как создать lazy-seq, который будет оцениваться шаг за шагом. Я хотел бы знать, как шаг за шагом оценить любой lazy-seq.
Я смущен, потому что запуск (chunked-seq? (gen-lazy-seq 10))
на gen-lazy-seq, как определено выше, ИЛИ, как определено в ответе JohnJ, оба возвращают false. Итак, проблема не может быть в том, что один создает последовательность фрагментов, а другой - нет.
В этом ответе показана функция seq1, которая превращает фрагментированный lazy-seq в фрагментарный. Использование этой функции по-прежнему вызывает ту же проблему с задержкой вывода. Я подумал, что, возможно, задержка связана с какой-то буферизацией в ответе, поэтому я попытался также распечатать время, когда реализуется каждый элемент в seq:
(defn seq1 [s]
(lazy-seq
(when-let [[x] (seq s)]
(cons x (seq1 (rest s))))))
(let [start-time (java.lang.System/currentTimeMillis)]
(doseq [item (seq1 (gen-lazy-seq 10))]
(let [elapsed-time (- (java.lang.System/currentTimeMillis) start-time)]
(println "time: " elapsed-time "item: " item))))
; output:
time: 10002 item: 1
time: 10002 item: 8
time: 10003 item: 9
time: 10003 item: 1
time: 10003 item: 7
time: 10003 item: 2
time: 10004 item: 0
time: 10004 item: 3
time: 10004 item: 5
time: 10004 item: 0
То же самое с версией gen-lazy-seq от JohnJ работает должным образом.
; output:
time: 1002 item: 4
time: 2002 item: 1
time: 3002 item: 6
time: 4002 item: 8
time: 5002 item: 8
time: 6002 item: 4
time: 7002 item: 5
time: 8002 item: 6
time: 9003 item: 1
time: 10003 item: 4
Изменить 2:
Эта проблема возникает не только с последовательностями, сгенерированными с помощью. Эта последовательность, сгенерированная с помощью map, не может быть обработана пошагово независимо от упаковки seq1:
(defn gen-lazy-seq [size]
(map (fn [_]
(Thread/sleep 1000)
(rand-int 10))
(range 0 size)))
Но эта последовательность, также созданная с помощью карты, работает:
(defn gen-lazy-seq [size]
(map (fn [_]
(Thread/sleep 1000)
(rand-int 10))
(repeat size :ignored)))
seq1
и не смог заставить его работать с Clojure 1.5 или с 1.2.1. Возможно, вы столкнулись с особенностями реализации макросаfor
, который не является прозрачным и который вы не можете просто отключить, заключив его в свой собственный ленивый seq. - person JohnJ   schedule 28.06.2013