如何建立一个块状的懒惰序列块?

问题描述

我想使用成组的缺点或其他方法来创建lazy-seq来阻止。给出来源:

(defn -source- [] (repeatedly (fn [] (future (Thread/sleep 100) [1 2]))))

(take 2 (-source-))
;; => (<future> <future>)

我想要一个名为injest函数,其中:

(take 3 (injest (-source-)))
=> [;; sleep 100
    1 2 
    ;; sleep 100
    1]

(take 6 (injest (-source-)))
=> [;; sleep 100
    1 2 
    ;; sleep 100
    1 2 
    ;; sleep 100
    1 2]

;; ... etc ...

我将如何编写此功能

解决方法

此来源在您消耗它时自然会被阻止,因此您不必做任何令人费解的事情。只需(mapcat deref)就足够了:

(doseq [x (take 16 (mapcat deref (-source- )))]
  (println {:value x :time (System/currentTimeMillis)}))
{:value 1,:time 1597725323091}
{:value 2,:time 1597725323092}
{:value 1,:time 1597725323092}
{:value 2,:time 1597725323093}
{:value 1,:time 1597725323093}
{:value 2,:time 1597725323194}
{:value 2,:time 1597725323195}
{:value 1,:time 1597725323299}
{:value 2,:time 1597725323300}
{:value 1,:time 1597725323406}
{:value 2,:time 1597725323406}
{:value 1,:time 1597725323510}
{:value 2,:time 1597725323511}

注意前几项是如何一次出现的,然后每对都错开了大约您期望的时间?这是由于众所周知的事实,出于性能原因,apply(因此mapcat(由apply concat实现)急于需要。如果即使在前几个项目上也要获得正确的延迟对您来说很重要,则可以简单地实现自己的apply concat版本,该版本无法针对简短的输入列表进行优化。

(defn ingest [xs]
  (when-let [coll (seq (map (comp seq deref) xs))]
    ((fn step [curr remaining]
       (lazy-seq
         (cond curr (cons (first curr) (step (next curr) remaining))
               remaining (step (first remaining) (next remaining)))))
      (first coll) (next coll))))

A。注释中的Webb提出了等效但更简单的实现:

(defn ingest [coll]
  (for [batch coll,item @batch]
    item))
,

我认为只要deref设置惰性seq的元素,您就很好,并且只需强制消耗所需的条目即可,例如:

(defn -source- [] (repeatedly (fn [] (future (Thread/sleep 100) [1 2]))))

(defn injest [src]
  (map deref src))

;; (time (dorun (take 3 (injest (-source-)))))
;; => "Elapsed time: 303.432003 msecs"

;; (time (dorun (take 6 (injest (-source-)))))
;; => "Elapsed time: 603.319103 msecs"

另一方面,我认为,根据项目的数量,最好避免创建大量的期货,而使用lazy-seq,这取决于元素的索引可能会阻塞一段时间。 / p>

,

您可以通过迭代状态机来解决。我不认为这会受到其他人指出的与apply相关的优化的影响,但是我不确定这种方法是否可能存在其他问题:

(defn step-state [[current-element-to-unpack input-seq]]
  (cond
    (empty? input-seq) nil
    (empty? current-element-to-unpack) [(deref (first input-seq)) (rest input-seq)]
    :default [(rest current-element-to-unpack) input-seq]))

(defn injest [input-seq]
  (->> [[] input-seq]
       (iterate step-state)
       (take-while some?)
       (map first)
       (filter seq)
       (map first)))