【问题标题】:Implementing Clojure conditional/branching transducer实现 Clojure 条件/分支转换器
【发布时间】:2016-03-05 00:12:21
【问题描述】:

我正在尝试在 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)))

有没有办法在不将整个输入序列存储在该转换器内的本地状态中的情况下制作这个分支/条件转换器(即在清理时在 1-arity 分支中进行所有处理)?

【问题讨论】:

    标签: clojure conditional transducer


    【解决方案1】:

    这个想法是在每次换能器切换时完成。 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 pos? (map #(* % 100)) (partition-all 2)) [-1 1 0]) 在您的示例中给出 ([-1] 100),这可能不是 OP 想要的
    • 你是对的。我需要删除 vresets 并且它正在工作。但现在我不明白为什么它不起作用了。 :(
    • 为什么arfbrf 需要是不稳定的——它们似乎没有改变?我认为由于我们无法缓冲的限制,这可能是最好的。不幸的是,这个解决方案在病态的情况下并不完全有效(例如,如果一个参数转换器缓冲所有内容并在 1-arg 完成步骤中完成所有工作)。如果有一个优雅的降级,那就太酷了,如果输入 xducer 都没有缓冲,那么if 不会缓冲,否则它会缓冲到所需的程度。不过,传感器并不神奇。
    • 我最初的实现是每次从a 切换到b(因此是易失性)时重新启动传感器,但它没有工作,我没有时间调试。
    【解决方案2】:

    我认为您的问题定义不明确。当传感器有状态时,你到底想发生什么?例如,您希望这样做:

    (sequence
      (if-xf even? (partition-all 3) (partition-all 2))
      (range 14))
    

    此外,有时减少函数在开始和结束时都有工作,不能随意重新启动。例如,这是一个计算均值的 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 效果很好,这就是您首先说明条件转换器的有用性的方式。

    【讨论】:

      猜你喜欢
      • 2014-06-18
      • 1970-01-01
      • 1970-01-01
      • 2019-06-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-12-24
      • 1970-01-01
      相关资源
      最近更新 更多