我想为生成无限结果序列的算法编写一个实现,其中每个元素表示算法的单个迭代的计算。使用延迟序列很方便,因为它解耦了实现的迭代次数(通过使用take
)和 burn-in 迭代(通过使用drop
)的逻辑
以下是两个算法实现的示例,一个生成惰性序列(yadda-lazy
),另一个不生成(yadda-loop
)。
(defn yadda-iter
[v1 v2 v3]
(+ (first v1)
(first v2)
(first v3)))
(defn yadda-lazy
[len]
(letfn [(inner [v1 v2 v3]
(cons (yadda-iter v1 v2 v3)
(lazy-seq (inner (rest v1)
(rest v2)
(rest v3)))))]
(let [base (cycle (range len))]
(inner base
(map #(* %1 %1) base)
(map #(* %1 %1 %1) base)))))
(defn yadda-loop
[len iters]
(let [base (cycle (range len))]
(loop [result nil
i 0
v1 base
v2 (map #(* %1 %1) base)
v3 (map #(* %1 %1 %1) base)]
(if (= i iters)
result
(recur (cons (yadda-iter v1 v2 v3) result)
(inc i)
(rest v1)
(rest v2)
(rest v3))))))
(prn (take 11 (yadda-lazy 4)))
(prn (yadda-loop 4 11))
有没有办法使用与loop
/ recur
相同的样式创建延迟序列?我更喜欢yadda-loop
,因为:
答案 0 :(得分:28)
你的循环版本会更好地写入(1)从循环中拉出加法,这样你就不必重复这么多的序列,并且(2)在向量累加器上使用conj
所以你的结果与您yadda-lazy
的顺序相同。
(defn yadda-loop-2 [len iters]
(let [v1 (cycle (range len))
v2 (map * v1 v1)
v3 (map * v1 v2)
s (map + v1 v2 v3)]
(loop [result [], s s, i 0]
(if (= i iters)
result
(recur (conj result (first s)), (rest s), (inc i))))))
然而,在这一点上,很明显循环是没有意义的,因为这只是
(defn yadda-loop-3 [len iters]
(let [v1 (cycle (range len))
v2 (map * v1 v1)
v3 (map * v1 v2)
s (map + v1 v2 v3)]
(into [] (take iters s))))
我们不妨提取iters
参数,只返回s
和take
。
(defn yadda-yadda [len]
(let [v1 (cycle (range len))
v2 (map * v1 v1)
v3 (map * v1 v2)]
(map + v1 v2 v3)))
这会产生与yadda-lazy
相同的结果,也是懒惰的,并且非常清晰
(take 11 (yadda-yadda 4)) ;=> (0 3 14 39 0 3 14 39 0 3 14)
您也可以等同地
(defn yadda-yadda [len]
(as-> (range len) s
(cycle s)
(take 3 (iterate (partial map * s) s))
(apply map + s)))
<强>附录强>
如果您正在寻找一种模式,用于将像您这样的热切循环转换为惰性序列
(loop [acc [] args args] ...)
- &gt; ((fn step [args] ...) args)
(if condition (recur ...) acc)
- &gt; (when condition (lazy-seq ...)
(recur (conj acc (f ...)) ...)
- &gt; (lazy-seq (cons (f ...) (step ...)))
将此应用于yadda-lazy
(defn yadda-lazy-2 [len iters]
(let [base (cycle (range len))]
((fn step [i, v1, v2, v3]
(when (< i iters)
(lazy-seq
(cons (yadda-iter v1 v2 v3)
(step (inc i), (rest v1), (rest v2), (rest v3))))))
0, base, (map #(* %1 %1) base), (map #(* %1 %1 %1) base))))
此时你可能想要取出iters
(defn yadda-lazy-3 [len]
(let [base (cycle (range len))]
((fn step [v1, v2, v3]
(lazy-seq
(cons (yadda-iter v1 v2 v3)
(step (rest v1), (rest v2), (rest v3)))))
base, (map #(* %1 %1) base), (map #(* %1 %1 %1) base))))
所以你可以
(take 11 (yadda-lazy-3 4)) ;=> (0 3 14 39 0 3 14 39 0 3 14)
然后你可能会说,嘿,我的yadda-iter
只是在第一个+
上应用step
而其余部分都应用v1, v2, v3
,所以为什么不合并我的(defn yadda-lazy-4 [len]
(let [base (cycle (range len))]
((fn step [vs]
(lazy-seq
(cons (apply + (map first vs))
(step (map rest vs)))))
[base, (map #(* %1 %1) base), (map #(* %1 %1 %1) base)])))
并使这一点更清楚?
(defn yadda-lazy-5 [len]
(let [base (cycle (range len))]
(map + base, (map #(* %1 %1) base), (map #(* %1 %1 %1) base))))
瞧,你刚刚重新实现了可变图谱
{{1}}
答案 1 :(得分:5)
@ A.Webb的答案是完美的,但是如果你对loop
/ recur
的爱情克服了他的论点,那么你知道你仍然可以结合两种递归方式。
例如,看看range
的实现:
(defn range
(...)
([start end step]
(lazy-seq
(let [b (chunk-buffer 32)
comp (cond (or (zero? step) (= start end)) not=
(pos? step) <
(neg? step) >)]
(loop [i start] ;; chunk building through loop/recur
(if (and (< (count b) 32)
(comp i end))
(do
(chunk-append b i)
(recur (+ i step)))
(chunk-cons (chunk b)
(when (comp i end)
(range i end step))))))))) ;; lazy recursive call
这是另一个例子,filter
的替代实现:
(defn filter [pred coll]
(letfn [(step [pred coll]
(when-let [[x & more] (seq coll)]
(if (pred x)
(cons x (lazy-seq (step pred more))) ;; lazy recursive call
(recur pred more))))] ;; eager recursive call
(lazy-seq (step pred coll))))
答案 2 :(得分:0)
The Tupelo library有一个新的lazy-gen
/ yield
功能,可以在Python中模仿generator functions
。它可以从循环结构中的任何点生成惰性序列。以下是显示yadda-loop
&amp;的lazy-gen
版本yield
正在行动中:
(ns tst.xyz
(:use clojure.test tupelo.test)
(:require [tupelo.core :as t] ))
(defn yadda-lazy-gen
[len iters]
(t/lazy-gen
(let [base (cycle (range len))]
(loop [i 0
v1 base
v2 (map #(* %1 %1) base)
v3 (map #(* %1 %1 %1) base)]
(when (< i iters)
(t/yield (yadda-iter v1 v2 v3))
(recur
(inc i)
(rest v1)
(rest v2)
(rest v3)))))))
Testing tst.clj.core
(take 11 (yadda-lazy 4)) => (0 3 14 39 0 3 14 39 0 3 14)
(yadda-loop 4 11) => (0 3 14 39 0 3 14 39 0 3 14)
(yadda-lazy-gen 4 11) => (0 3 14 39 0 3 14 39 0 3 14)
Ran 1 tests containing 1 assertions.
0 failures, 0 errors.