([f coll]
(lazy-seq
(when-let [s (seq coll)]
(let [fst (first s)
fv (f fst)
run (cons fst (take-while #(= fv (f %)) (next s)))]
(cons run (partition-by f (lazy-seq (drop (count run) s))))))))
看起来f
在绑定fv
时被调用1次。由于take-while
部分不会产生任何类似的键,所以run
的值是'(1)。然而,f
"曾经"在s的其余部分的第一值上被调用,因此2被打印1次。当在懒序列构建中,partition-by伪递归时,新分区的第一个参数现在是2。《run》的值是通过在2上调用f
确定的,所以打印出两个,等等。这个过程重复。
传感器变体绝不会递归地通过下一个分区的头部,因此它仅在每个原始输入元素上调用一次 f
。
注意:我们可以这样得出一个不需要状态的1次分区函数变体
user> (defn partition-by-once [f coll]
(->> coll
(map (fn [x] [(f x) x]))
(partition-by first)
(map #(map second %))))
#'user/partition-by-once
user> (partition-by-once #(do (println %)
(rand)) [1 2 3])
1
2
3
((1) (2) (3))
我们也可以实现传感器使用的易失性技巧
(defn partition-by-once-statefully
[f coll]
(let [pv (volatile! ::none)
test (fn [v] (vreset! pv (f v)))
aux (fn aux
([coll]
(lazy-seq
(when-let [s (seq coll)]
(let [fst (first coll)
fv @pv
run (cons fst (take-while #(= fv (test %)) (next s)))]
(cons run (aux (lazy-seq (drop (count run) s))))))))
([fst remaining]
(if-let [s (seq remaining)]
(let [fv (test fst)
run (cons fst (take-while #(= fv (test %)) s))]
(cons run (aux (lazy-seq (drop (dec (count run)) s)))))
`((~fst)))))]
(when-let [s (seq coll)]
(aux (first coll) (next coll)))))
user> (doall (partition-by-once-vol (fn [v] (println v) (odd? v)) [1 2 3 5 7 8 10 11]))
1
2
3
5
7
8
10
11
((1) (2) (3 5 7) (8 10) (11))