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

欢迎!请参阅关于页面了解有关此内容的更多信息。

0
序列

大家好,

我听说如果在语言中加入transducers之前,它们将成为所有序列懒操作的基本构建块。由于它们是后来加入的,所以您需要稍微调整您的代码以使用transducers。而且我在想,为什么使用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
by

本例假设使用所有的结果。eductions不是序列,在计算时延迟但不缓存(根据文档字符串:“请注意,每次调用reduce/iterator时都将执行这些应用”),它们不是懒的,所以这会产生很多影响(对于并发以及何时以及如何执行工作以及内存消耗)。

如果您只想完成所需的工作量,eductions或reduce并不是很好的选择,因为它们会完成所有工作。《sequence》函数(使用transducers)可以为您提供增量计算的一些方面,但在面对扩展的transducers(如mapcat)时不如序列具有懒加载性。

很难设想如果没有先存在transducer可能会产生的设计后果。也许Clojure会更愿意并且更加重视reduce,会更容易选择懒加载的替代方案而不是反过来。


编辑了
> 如果你只想完成所需的工作量,那么eductions或reduce并不是很好的选择,因为它们会完成所有的工作。

我曾经以为eductions只会完成所需的工作量(或者说,"消费者请求"的工作量)。

(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]是延迟的
> 它常用于贪婪场景
> 它是基于迭代器的,但经常与贪婪的reduce一起使用

更多细节在https://clojurians.slack.com/archives/C03S1KBA2/p1681394119498109?thread_ts=1681383034.191669&cid=C03S1KBA2的线程中
...