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不是很好的选项,因为它们会执行所有的工作。《code>sequence函数(带有转换器)可以为您提供增量计算的一些方面,但面对扩展转换器(如mapcat)时,它不如序列那样懒惰。

很难想出如果转换器一开始就存在,可能会导致的设计后果的集合。也许Clojure将更加热烈,以reduce为主,而不是转向懒惰的替代方案。


编辑
> 如果你只想做必需的工作量,eductions 或 reduce 不是很好的选项,因为它们会完成所有工作

我有一种印象,认为eductions只会做需要的工作量(或者说,由eductions的消费方“请求”的工作量)

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

  (into [] (take 3) ed)

  (println "ended"))
starting
0
100
1
101
2
102
ended
=> nil

这里有什么明显的缺陷吗,Alex?
Alex 在 Slack 上的澄清

> 我在那句话里确实指的是 reduce
> [eduction] 是延迟执行的
> 它常用于饥饿场景
> 它是基于迭代器的,但经常与饥饿的 reduce 一起使用

更多细节请参阅 https://clojurians.slack.com/archives/C03S1KBA2/p1681394119498109?thread_ts=1681383034.191669&cid=C03S1KBA2
...