实现Clojure条件/分支转换器

实现Clojure条件/分支转换器,clojure,conditional,transducer,Clojure,Conditional,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)

我试图在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)))
如果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)))

有没有一种方法可以在不将整个输入序列以本地状态存储在该传感器内的情况下制作该分支/有条件传感器(即,在清理时在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))
此外,有时还原函数在开始和结束时都有工作要做,不能随意重新启动。例如,这里有一个计算平均值的减速机:

(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
(传感器
(如果是xf
(fn[x](

我的回答是:我认为你最初的解决方案是最好的。使用
map
,它工作得很好,这就是您首先说明条件转换器有用性的方式。

(序列(如果xf pos?(map#(*%100))(分区所有2))[-11 10])
给出了
([-1]100)
在您的示例中,这可能不是您想要的。我需要删除
vresets
,它正在工作。但现在我不明白为什么它不起作用了(为什么
arf
brf
需要是易变的——它们似乎没有变化?我认为在我们不能缓冲的限制下,这可能是最好的解决方案。不幸的是,这种解决方案在病理情况下不太有效,(例如,如果其中一个变元传感器缓冲所有内容并完成1-arg完成步骤中的所有工作)。如果出现优雅的降级,那就太酷了,如果输入xducer都没有缓冲,那么
if
就不会缓冲,否则它会缓冲到所需的程度。不过,传感器还不是很神奇。我最初的实现是每次从
a
切换到
b (因此是volatile)但它不工作,我没有时间调试。