通过连接集合创建惰性序列
Create Lazy Sequency by Concatenating Collections
通过连接集合创建惰性序列。
考虑以下函数:
(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 请求,这是不可接受的。
惰性序列元素的 "unneeded" 实现正在发生,因为 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
怎么样
(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
使用了它。
它最多实现 4 个参数 [^clojure.lang.IFn f a b c d & args]
给定多个参数。
通过连接集合创建惰性序列。
考虑以下函数:
(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 请求,这是不可接受的。
惰性序列元素的 "unneeded" 实现正在发生,因为 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
怎么样
(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
使用了它。
它最多实现 4 个参数 [^clojure.lang.IFn f a b c d & args]
给定多个参数。