实施 Clojure conditional/branching 转换器
Implementing Clojure conditional/branching transducer
我正尝试在 Clojure 中制作一个条件转换器,如下所示:
(defn if-xf
"Takes a predicate and two transducers.
Returns a new transducer that routes the input to one of the transducers
depending on the result of the predicate."
[pred a b]
(fn [rf]
(let [arf (a rf)
brf (b rf)]
(fn
([] (rf))
([result]
(rf result))
([result input]
(if (pred input)
(arf result input)
(brf result input)))))))
它非常有用,因为它可以让你做这样的事情:
;; multiply odd numbers by 100, square the evens.
(= [0 100 4 300 16 500 36 700 64 900]
(sequence
(if-xf odd? (map #(* % 100)) (map (fn [x] (* x x))))
(range 10)))
但是,此条件转换器不能很好地与在其 1-arity 分支中执行清理的转换器一起工作:
;; negs are multiplied by 100, non-negs are partitioned by 2
;; BUT! where did 6 go?
;; expected: [-600 -500 -400 -300 -200 -100 [0 1] [2 3] [4 5] [6]]
;;
(= [-600 -500 -400 -300 -200 -100 [0 1] [2 3] [4 5]]
(sequence
(if-xf neg? (map #(* % 100)) (partition-all 2))
(range -6 7)))
是否可以调整 if-xf
的定义来处理带清理的换能器的情况?
我正在尝试这个,但有奇怪的行为:
(defn if-xf
"Takes a predicate and two transducers.
Returns a new transducer that routes the input to one of the transducers
depending on the result of the predicate."
[pred a b]
(fn [rf]
(let [arf (a rf)
brf (b rf)]
(fn
([] (rf))
([result]
(arf result) ;; new!
(brf result) ;; new!
(rf result))
([result input]
(if (pred input)
(arf result input)
(brf result input)))))))
具体来说,冲洗发生在最后:
;; the [0] at the end should appear just before the 100.
(= [[-6 -5] [-4 -3] [-2 -1] 100 200 300 400 500 600 [0]]
(sequence
(if-xf pos? (map #(* % 100)) (partition-all 2))
(range -6 7)))
有没有一种方法可以使这个 branching/conditional 转换器无需将整个输入序列存储在该转换器的本地状态中(即在清理时在 1-arity 分支中进行所有处理)?
思路是每次换能器切换完成。 IMO 这是唯一无需缓冲的方法:
(defn if-xf
"Takes a predicate and two transducers.
Returns a new transducer that routes the input to one of the transducers
depending on the result of the predicate."
[pred a b]
(fn [rf]
(let [arf (volatile! (a rf))
brf (volatile! (b rf))
a? (volatile! nil)]
(fn
([] (rf))
([result]
(let [crf (if @a? @arf @brf)]
(-> result crf rf)))
([result input]
(let [p? (pred input)
[xrf crf] (if p? [@arf @brf] [@brf @arf])
switched? (some-> @a? (not= p?))]
(if switched?
(-> result crf (xrf input))
(xrf result input))
(vreset! a? p?)))))))
(sequence (if-xf pos? (map #(* % 100)) (partition-all 2)) [0 1 0 1 0 0 0 1])
; => ([0] 100 [0] 100 [0 0] [0] 100)
我认为你的问题定义不明确。当传感器有状态时,你到底想发生什么?例如,您希望这样做:
(sequence
(if-xf even? (partition-all 3) (partition-all 2))
(range 14))
此外,有时reducing函数在开始和结束时都有工作要做,不能随意重启。例如,这是一个计算均值的 reducer:
(defn mean
([] {:count 0, :sum 0})
([result] (double (/ (:sum result) (:count result))))
([result x]
(update-in
(update-in result [:count] inc)
[:sum] (partial + x))))
(transduce identity mean [10 20 40 40]) ;27.5
现在我们取平均值,低于 20 的都算 20,但其他的都减 1:
(transduce
(if-xf
(fn [x] (< x 20))
(map (constantly 20))
(map dec))
mean [10 20 40 40]) ;29.25
我的回答如下:我认为你原来的解决方案是最好的。它使用 map
效果很好,这就是您首先陈述条件传感器有用性的方式。
我正尝试在 Clojure 中制作一个条件转换器,如下所示:
(defn if-xf
"Takes a predicate and two transducers.
Returns a new transducer that routes the input to one of the transducers
depending on the result of the predicate."
[pred a b]
(fn [rf]
(let [arf (a rf)
brf (b rf)]
(fn
([] (rf))
([result]
(rf result))
([result input]
(if (pred input)
(arf result input)
(brf result input)))))))
它非常有用,因为它可以让你做这样的事情:
;; multiply odd numbers by 100, square the evens.
(= [0 100 4 300 16 500 36 700 64 900]
(sequence
(if-xf odd? (map #(* % 100)) (map (fn [x] (* x x))))
(range 10)))
但是,此条件转换器不能很好地与在其 1-arity 分支中执行清理的转换器一起工作:
;; negs are multiplied by 100, non-negs are partitioned by 2
;; BUT! where did 6 go?
;; expected: [-600 -500 -400 -300 -200 -100 [0 1] [2 3] [4 5] [6]]
;;
(= [-600 -500 -400 -300 -200 -100 [0 1] [2 3] [4 5]]
(sequence
(if-xf neg? (map #(* % 100)) (partition-all 2))
(range -6 7)))
是否可以调整 if-xf
的定义来处理带清理的换能器的情况?
我正在尝试这个,但有奇怪的行为:
(defn if-xf
"Takes a predicate and two transducers.
Returns a new transducer that routes the input to one of the transducers
depending on the result of the predicate."
[pred a b]
(fn [rf]
(let [arf (a rf)
brf (b rf)]
(fn
([] (rf))
([result]
(arf result) ;; new!
(brf result) ;; new!
(rf result))
([result input]
(if (pred input)
(arf result input)
(brf result input)))))))
具体来说,冲洗发生在最后:
;; the [0] at the end should appear just before the 100.
(= [[-6 -5] [-4 -3] [-2 -1] 100 200 300 400 500 600 [0]]
(sequence
(if-xf pos? (map #(* % 100)) (partition-all 2))
(range -6 7)))
有没有一种方法可以使这个 branching/conditional 转换器无需将整个输入序列存储在该转换器的本地状态中(即在清理时在 1-arity 分支中进行所有处理)?
思路是每次换能器切换完成。 IMO 这是唯一无需缓冲的方法:
(defn if-xf
"Takes a predicate and two transducers.
Returns a new transducer that routes the input to one of the transducers
depending on the result of the predicate."
[pred a b]
(fn [rf]
(let [arf (volatile! (a rf))
brf (volatile! (b rf))
a? (volatile! nil)]
(fn
([] (rf))
([result]
(let [crf (if @a? @arf @brf)]
(-> result crf rf)))
([result input]
(let [p? (pred input)
[xrf crf] (if p? [@arf @brf] [@brf @arf])
switched? (some-> @a? (not= p?))]
(if switched?
(-> result crf (xrf input))
(xrf result input))
(vreset! a? p?)))))))
(sequence (if-xf pos? (map #(* % 100)) (partition-all 2)) [0 1 0 1 0 0 0 1])
; => ([0] 100 [0] 100 [0 0] [0] 100)
我认为你的问题定义不明确。当传感器有状态时,你到底想发生什么?例如,您希望这样做:
(sequence
(if-xf even? (partition-all 3) (partition-all 2))
(range 14))
此外,有时reducing函数在开始和结束时都有工作要做,不能随意重启。例如,这是一个计算均值的 reducer:
(defn mean
([] {:count 0, :sum 0})
([result] (double (/ (:sum result) (:count result))))
([result x]
(update-in
(update-in result [:count] inc)
[:sum] (partial + x))))
(transduce identity mean [10 20 40 40]) ;27.5
现在我们取平均值,低于 20 的都算 20,但其他的都减 1:
(transduce
(if-xf
(fn [x] (< x 20))
(map (constantly 20))
(map dec))
mean [10 20 40 40]) ;29.25
我的回答如下:我认为你原来的解决方案是最好的。它使用 map
效果很好,这就是您首先陈述条件传感器有用性的方式。