我可以一步一步地处理未实现的lazy-seq

时间:2013-06-26 13:43:00

标签: clojure lazy-evaluation lazy-sequences

我有一个lazy-seq,每个项目需要一些时间来计算:

(defn gen-lazy-seq [size]
  (for [i (range size)]
    (do
      (Thread/sleep 1000)
      (rand-int 10))))

是否可以逐步评估此序列并打印结果。当我尝试使用fordoseq clojure处理它时,总是会在打印任何内容之前实现整个lazy-seq:

(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。

我很困惑,因为如上所述在gen-lazy-seq上运行(chunked-seq? (gen-lazy-seq 10))或者在JohnJ的答案中定义的都返回false。那么问题不在于一个人创建一个分块序列而另一个不会。

this回答中,显示了一个函数seq1,它将一个chunked lazy-seq变成一个非chunked的seq1。尝试该功能仍然会产生延迟输出的相同问题。我认为延迟可能与repl中的某种缓冲有关,所以我也尝试打印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

对JohnJ的gen-lazy-seq版本做同样的事情按预期工作

; 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:

不仅生成的序列有这个问题。无论seq1包装如何,都无法逐步处理使用map生成的序列:

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

1 个答案:

答案 0 :(得分:4)

Clojure的懒惰序列经常被分块。如果你使用大size s(在这种情况下减少线程休眠时间将有所帮助),你可以在你的例子中看到工作中的分块。另请参阅these related SO posts

虽然for似乎已被分块,但以下内容并未按预期运行:

(defn gen-lazy-seq [size]
  (take size (repeatedly #(do (Thread/sleep 1000)
                              (rand-int 10)))))

(doseq [item (gen-lazy-seq 10)]
  (println item)) 

“我有消息通过网络进入,我想在收到所有消息之前开始处理它们。”如果你懒得处理它们,实际上应该是这种情况。