通过连接集合创建延迟频率

时间:2017-01-12 15:04:17

标签: clojure lazy-sequences

通过连接集合创建一个惰性序列。

考虑以下功能:

(defn req []
  (Thread/sleep 1000)
  (repeat 4 (rand-int 10)))

由于函数最终将成为http请求,因此会添加睡眠,因此它应该模拟延迟。

示例输出:

(req)
;; (8 8 8 8)

(req)
;; (4 4 4 4)

我现在正在考虑一个函数,它通过后续req结果的连接创建一个懒惰的序列构建。

(take 10 (f req))
;; (3 3 3 3 2 2 2 2 9 9)

这是一个实现:

(defn f [g]
  (lazy-seq (concat (g) (f g))))

这是要走的路吗?我以某种方式猜测可能已经有了这个可用的抽象..我尝试了lazy-cat,但这个宏似乎只适用于固定数量的给定序列。

事实证明这是一个有效的抽象:

(take 10 (apply concat (repeatedly req)))

然而,看起来像懒惰序列的分块会导致req被调用的频率超过此处所需,如果它是一个http请求,这是不可接受的。

2 个答案:

答案 0 :(得分:3)

懒惰序列元素的“不必要”实现正在发生,因为apply needs to know传递了函数的参数数量。

快速浏览一下Clojure核心库,似乎它没有提供连接序列序列的功能,同时以你想要的方式处理懒惰(不会冗余地实现传递的懒惰序列的元素),所以,你需要自己实现它。

这是可能的解决方案:

(defn apply-concat [xs]
  (lazy-seq
   (when-let [s (seq xs)]
     (concat (first s) (apply-concat (rest s))))))

然后:

user> (defn req []
        (println "--> making request")
        [1 2 3 4])
#'user/req
user> (dorun (take 4 (apply-concat (repeatedly req))))
--> making request
nil
user> (dorun (take 5 (apply-concat (repeatedly req))))
--> making request
--> making request
nil
user> (dorun (take 8 (apply-concat (repeatedly req))))
--> making request
--> making request
nil
user> (dorun (take 9 (apply-concat (repeatedly req))))
--> making request
--> making request
--> making request
nil

这种方法的唯一问题是破坏堆栈的危险,因为apply-concat可能无限递归。

更新

准确地说apply实现了传递的延迟序列的(arity of passed function + 1)元素:

user> (dorun (take 1 (apply (fn [& xs] xs) (repeatedly req))))
--> making request
--> making request
nil
user> (dorun (take 1 (apply (fn [x & xs] xs) (repeatedly req))))
--> making request
--> making request
--> making request
nil
user> (dorun (take 1 (apply (fn [x y & xs] xs) (repeatedly req))))
--> making request
--> making request
--> making request
--> making request
nil

答案 1 :(得分:2)

怎么样

(take 14 
  (mapcat identity (repeatedly req)))

说明:

(defn req [] 
  (print ".") 
  (repeat 4 (rand-int 10)))

(def x 
  (take 80 (mapcat identity (repeatedly req))))
; prints .... = 4x ; this is probably some repl eagerness

; to take 80 items, 20 realizatons (of 4 items) are requrend 
(def y 
  (doall
    (take 80 (mapcat identity (repeatedly req))))) 
 ; prints ..................... = 21x 

编辑:关于那4个早期实现:

我认为这是apply,我们mapcat使用了[^clojure.lang.IFn f a b c d & args]。 它实现了最多4个args constraints = { audio: false, video: { width: 400, height: 300, deviceId: deviceId ? {exact: deviceId} : undefined } }; 给出了多个。{/ p>