来自外部饲料的无限懒惰事件序列

时间:2015-02-10 00:14:28

标签: clojure

假设我有一个函数(get-events "feed"),它按时间顺序返回一个事件向量,取自外部源。

现在,在任何给定时刻,该函数都会返回截至该时间点的事件列表。几秒钟后,当饲料不断增长时,它会再返回一些事件等。

如果我想创建一个永远从feed中提取新事件的lazy-seq,确保它不会重复那些已经看过的事件,我该怎么写呢?当我不使用recur时,我遇到堆栈溢出错误,但我不能使用recur,因为它不会出现在尾部位置。

(def continually-list-events
  ([feed] (continually-list-events feed (hash-set)))
  ([feed seen]
   (let [events-now (get-events feed)]
     (into (remove seen events-now)
           (lazy-seq
             (continually-list-events feed
                                      (into seen events-now))))))

你可以看到我正在尝试使用累加器来跟踪已经看过的事件(在一组中),并且我确保总是过滤掉我见过的那些事件。

1 个答案:

答案 0 :(得分:2)

如果每个步骤都跟踪到目前为止已收到的事件数,那么该迭代可以通过删除旧事件来返回一系列新事件。

user> (->> (iterate (fn [[events-so-far contents]]
                      (let [events (get-events)
                            new-events (drop events-so-far events)]
                        [(count events) new-events])))
           (mapcat second))

然后,您可以从序列中删除计数,并将事件块整合为一系列单个事件。

在您的示例中,stackoverflow是因为在调用cons之后没有调用lazy-seq所以它正在计算整个列表序列中的第一项。

user> (defn example [x] (lazy-seq (cons x (example (inc x)))))
#'user/example
user> (take 5 (example 4))
(4 5 6 7 8)
user> (defn example [x] (lazy-seq  (example (inc x))))
#'user/example
user> (take 5 (example 4))
... long pause then out of memory ...

PS:直接使用lazy-seq有点不常见,但知道它是如何工作的很重要。