分享您的见解,请在2024 年 Clojure 调查问卷!

欢迎!请查看关于页面以了解更多有关此信息的工作方式。

0
序列

你好,

我听说如果之前在语言中包含转换器,它们会被用作所有序列懒操作的构建块。由于后来才添加,所以需要稍微修改代码以使用转换器。我想知道为什么不使用eduction?我正在试图理解在这种情况下不定义序列懒操作的性能优势(当然假设行为相同)

(defn map
  ([f] ;; the standard transducer definition
   ,,,)
  ([f coll]
   (eduction (map f) coll))

我正在试图理解基于以下假设(可能是一个错误的假设):

(->> (range 5000000)
     (eduction (map inc))
     (eduction (filter odd?))
     (eduction (map dec))
     (eduction (filter even?))
     (eduction (map (fn [n] (+ 3 n))))
     (eduction (filter odd?))
     (eduction (map inc))
     (eduction (filter odd?))
     (eduction (map dec))
     (eduction (filter even?))
     (eduction (map (fn [n] (+ 3 n))))
     (eduction (filter odd?))
     (into []))

与以下相同

(->> (range 5000000)
     (map inc)
     (filter odd?)
     (map dec)
     (filter even?)
     (map (fn [n] (+ 3 n)))
     (filter odd?)
     (map inc)
     (filter odd?)
     (map dec)
     (filter even?)
     (map (fn [n] (+ 3 n)))
     (filter odd?)
     (into []))

1 个答案

+3

此示例假设使用所有的结果。eductions 不是序列,计算时延迟但不会缓存(根据文档字符串:“请注意,每次调用 reduce/iterator 时都会执行这些应用”),并且不是懒的,因此这对并发以及工作执行和内存消耗的方式和时机有很多影响。

如果你只想完成所需的工作量,eductions 或 reduce 不是很好的选择,因为它们会完成所有的工作。带有转换器的 sequence 函数可以给你一些增量计算的方面,但在面对像 mapcat 这样的扩展转换器时,它的懒性不如序列。

很难想象出如果继电器先存在,可能导致的设计后果集合。也许Clojure会更渴望,以 gradual-reduction 为主,并转向惰性替代方案,而不是反过来。

> 如果只想做必要的工作量,eductions 或 reduce 不是很好的选择,因为它们会做所有工作

我觉得eductions只会做必要的工作量(或者更确切地说,“eduction”消费者请求的工作量)

(let [ed (->> (range 5000000)
              (eduction (map #(doto % println)))
             (eduction (map #(doto % (-> (+ 100) println)))]
  (println "taking")

  (into [] (take 3) ed)

  (println "taken")
taking
0
100
1
101
2
102
taken
=> nil

我在这里遗漏了什么明显的点,Alex?
Alex 在 Slack 上的说明

> 我在这里实际上是提到 reduce
> 它 [eduction] 是延迟的
> 它通常用于 eager 场景
> 它基于迭代器,但通常与 eager 的 reduce 一起使用

更多详细信息请查阅 https://clojurians.slack.com/archives/C03S1KBA2/p1681394119498109?thread_ts=1681383034.191669&cid=C03S1KBA2
...