2024 年 Clojure 调查 中分享您的想法!

欢迎!请查看 关于页面 获取有关如何使用此工具的更多信息。

+1
Clojure
(let [coll [{:a 1 :b 2}
            {:a 3 :b 4}
            {:a 7 :b 5}
            {:a 1 :b 4}]
      xf1 (comp (map :a) (filter #{1 3}))
      xf2 (comp (map :b) (filter #{2 4}))]
  (concat
    (into [] xf1 coll)
    (into [] xf2 coll)))

有办法将上面的两个 xforms (xf1 和 xf2) 以某种方式组合成一个,以便在 coll 集合中只遍历一次吗?

4 个回答

0

这并不真的是 transducers 的组合,但

(let [coll [{:a 1 :b 2}
            {:a 3 :b 4}
            {:a 7 :b 5}
            {:a 1 :b 4}]
      xf1 #(-> % :a #{1 3})
      xf2 #(-> % :b #{2 4})] 
  (into []
    (comp 
      (mapcat (juxt xf1 xf2))
      (remove nil?))
    coll))
=> [1 2 3 4 1 4]
很好,但我正在寻找将 transducers 合并的方法...
0
  (let [coll [{:a 1 :b 2}
              {:a 3 :b 4}
              {:a 7 :b 5}
              {:a 1 :b 4}]
        xf1 (comp (map :a) (filter #{1 3}))
        xf2 (comp (map :b) (filter #{2 4}))]
    (eduction cat [(eduction xf1 coll) (eduction xf2 coll)]))

eduction 可以被替换为 sequence

0
(defn facet [m]
  (fn [f]
    (let [m (into {} (for [[k v] m]
                       [k (v f)]))]
      (fn
        ([accum]
         (reduce
          (fn [accum1 [k accum2]]
            (assoc accum1 k ((get m k) accum2)))
          accum
          accum))
        ([accum value]
         (reduce
          (fn [accum1 [k accum2]]
            (assoc accum1 k ((get m k) accum2 value)))
          accum
          accum))))))

(let [coll [{:a 1 :b 2}
            {:a 3 :b 4}
            {:a 7 :b 5}
            {:a 1 :b 4}]
      xf1 (comp (map :a) (filter #{1 3}))
      xf2 (comp (map :b) (filter #{2 4}))]
  ((comp (partial apply concat)
         (juxt :a :b))
   (transduce
    (facet {:a xf1
            :b xf1})
    conj
    {:a []
     :b []}
    coll)))
有趣转换器的查找地址为:[https://github.com/cgrand/xforms](https://github.com/cgrand/xforms),而不同类型的处理折叠的灵感来源可参考:[https://github.com/aphyr/tesser](https://github.com/aphyr/tesser)
0

编辑

哎呀,我突然想起我实际上并没有回答你的问题。我将我的原始问题移动到评论区。

使用 cgrand/xforms,可能的解答可能是:

(let [coll [{:a 1 :b 2}
            {:a 3 :b 4}
            {:a 7 :b 5}
            {:a 1 :b 4}]
      xf1 (comp (map :a) (filter #{1 3}))
      xf2 (comp (map :b) (filter #{2 4}))]
  (= (concat
      (into [] xf1 coll)
      (into [] xf2 coll))
     
     ;; do it in one pass
     (x/into []
       (comp (x/transjuxt [(comp xf1 (x/into [])) (comp xf2 (x/into []))])
             cat
             cat)
       coll)))
这里的搜索功能真是个好消息。我最近遇到了相似的情况,其中需要对多个输入集合执行不同的转换并最终得到一个集合,所以我试验了几种不同的方法。

这是我对于该问题的REPL会话。经过一些基本的性能测试后,我得出结论,将集合通过多个 `into` 讨论的解决方案可能是最合乎习惯的,并且在性能上并不比 `catduce` 方法差。

    (def input1 (into [] (range 100000)))
    (def input2 (into [] (range 999 9999)))
    (定义 xf1 为过滤奇数)
    (定义 xf2 为映射增 1)
    (定义 xf3 为取前 100 个元素)
    (定义 xf4 为组合(filter 偶数)(map 减 1)(取前 1000 个元素))
  
    ;;; 连接(不喜欢它惰性,并且分配了多个中间集合)
    (定义 result 为入[]
                      (concat
                       (入[] xf1 input1)
                       (入[] xf2 input1)
                       (入[] xf3 input2)
                       (入[] xf4 input2))
  
    ;;; cat(仍分配中间集合)
    (= result
       (入[]
             cat
             [(入[] xf1 input1)
              (入[] xf2 input1)
              (入[] xf3 input2)
              (入[] xf4 input2)])))
  
    ;;; intos(无中间分配,但有多个临时/持久的转换)
    .;    (分号注释:- 还好,因为它们都是 O(1) 的)
    (= result
       (箭头 []
             (入[] xf1 input1)
             (入[] xf2 input1)
             (入[] xf3 input2)
             (入[] xf4 input2))
  
    ;;; reduce into
    ;;; - 几乎与 intos 解决方案相同,无需多次写入 into
    (= result
       (reduce
         #(入[] %1 (second %2) (first %2))
         []
         [[input1 xf1]
          [input1 xf2]
          [input2 xf3]
          [input2 xf4]]))
  
    ;;; catduce
    ;;; - 尝试在不进行许多临时/持久转换的情况下完成它
    (defn catduce
      "从 clojure.core/cat 采用")
      [rf]
      (fn
        ([] (rf))
        ([result] (rf result))
        ([result input+xf]
             (transduce (second input+xf) rf result (first input+xf)))))
  
    (= result
       (入[] catduce
             [[input1 xf1]
              [input1 xf2]
              [input2 xf3]
              [input2 xf4]])))
...