3

我有一个惰性序列,其中每个项目都需要一些时间来计算:

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

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

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

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

这两个表达式都将等待 10 秒,然后再打印出任何内容。我已经将 doall 和 dorun 视为一种解决方案,但它们要求惰性序列生成函数包含 println。我想分别定义一个惰性序列生成函数和惰性序列打印函数,并让它们逐项一起工作。

尝试这样做的动机:我有来自网络的消息,我想在收到所有消息之前开始处理它们。同时,将与查询对应的所有消息保存在惰性序列中会很好。

编辑1:

JohnJ 的回答展示了如何创建一个将逐步评估的惰性序列。我想知道如何逐步评估任何惰性序列。

我很困惑,因为(chunked-seq? (gen-lazy-seq 10))在上面定义的 gen-lazy-seq 或 JohnJ 的答案中定义的运行都返回 false。因此,问题不可能是一个创建分块序列而另一个没有。

这个答案中,显示了一个函数 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)))
4

1 回答 1

4

Clojure 的惰性序列通常是分块的。如果您采用较大的 s,您可以在示例中看到分块的工作size(在这种情况下,这将有助于减少线程睡眠时间)。另请参阅这些相关的 SO 帖子

尽管for似乎是分块的,但以下内容不是并且可以按需要工作:

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

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

“我有来自网络的消息,我想在收到所有消息之前开始处理它们。” 分块与否,如果您懒惰地处理它们,实际上应该是这种情况。

于 2013-06-26T15:22:15.050 回答