有人能简单地给我解释一下Clojure传感器吗?

有人能简单地给我解释一下Clojure传感器吗?,clojure,transducer,Clojure,Transducer,我已经试着阅读了这篇文章,但我仍然不理解它们的价值,也不知道它们取代了什么。他们会让我的代码更短,更容易理解吗 更新 很多人都给出了答案,但如果能看到一些简单的例子,比如有传感器和没有传感器,那就太好了,即使像我这样的白痴也能理解。当然,除非换能器需要一定程度的理解,在这种情况下,我永远不会理解它们:(换能器可以提高效率,并允许您以更模块化的方式编写高效的代码 与编写对旧的映射、过滤器、减少等的调用相比,您可以获得更好的性能,因为您不需要在每个步骤之间构建中间集合,并重复遍历这些集合 与简化程

我已经试着阅读了这篇文章,但我仍然不理解它们的价值,也不知道它们取代了什么。他们会让我的代码更短,更容易理解吗

更新
很多人都给出了答案,但如果能看到一些简单的例子,比如有传感器和没有传感器,那就太好了,即使像我这样的白痴也能理解。当然,除非换能器需要一定程度的理解,在这种情况下,我永远不会理解它们:(

换能器可以提高效率,并允许您以更模块化的方式编写高效的代码

与编写对旧的
映射
过滤器
减少
等的调用相比,您可以获得更好的性能,因为您不需要在每个步骤之间构建中间集合,并重复遍历这些集合


与简化程序或手动将所有操作组合成一个表达式相比,您更容易使用抽象、更好的模块化和处理函数的重用。

我发现阅读的示例有助于我具体了解如何在日常代码中使用它们

例如,考虑这个例子(从上面链接的自述文件中获取):

首先,使用
xf
看起来比通常使用下划线的方法要干净得多

_.filter(_.map([0, 1, 2, 3, 4], inc), isEven);

假设您想使用一系列函数来转换数据流

cat /etc/passwd | tr '[:lower:]' '[:upper:]' | cut -d: -f1| grep R| wc -l
(上面的命令统计用户名中字母r为大写或小写的用户数)。这是作为一组进程实现的,每个进程都从前面进程的输出中读取,因此有四个中间流。您可以想象一个不同的实现,它将五个命令组合成一个聚合命令,该聚合命令将从其输入读取并写入其输出正好一次。如果中间流如果你的手机价格昂贵,而且合成成本低廉,这可能是一个很好的折衷方案

Clojure也有同样的情况。有多种方式来表示转换管道,但取决于您的操作方式,最终可能会出现从一个函数传递到下一个函数的中间流。如果您有大量数据,则将这些函数合成为单个函数的速度会更快。转换器使实现转换变得容易在早期的Clojure创新中,reducers也允许您这样做,但有一些限制。传感器消除了一些限制

因此,为了回答您的问题,转换器不一定会使您的代码更短或更容易理解,但您的代码可能也不会更长或更难理解,而且如果您处理大量数据,转换器可以使您的代码更快


是传感器的一个很好的概述。

传感器是如何处理数据序列的配方,而不知道底层序列是什么(如何操作)。它可以是任何顺序、异步通道,也可以是可观察的

(transduce (filter #(not (.hasOwnProperty prevChildMapping %))) (.-push #js[]) #js [] nextKeys)
它们是可组合和多态的

好处是,您不必在每次添加新数据源时实现所有标准组合器。一次又一次。因此,作为用户,您可以在不同的数据源上重用这些配方

在Clojure版本1.7之前,您有三种编写数据流查询的方法:

  • 嵌套调用

    (reduce + (filter odd? (map #(+ 2 %) (range 0 10))))
    
  • 功能成分

    (def xform
      (comp
        (partial filter odd?)
        (partial map #(+ 2 %))))
    (reduce + (xform (range 0 10)))
    
  • 线程宏

    (defn xform [xs]
      (->> xs
           (map #(+ 2 %))
           (filter odd?)))
    (reduce + (xform (range 0 10)))
    
  • 使用传感器,您可以像这样编写:

    (def xform
      (comp
        (map #(+ 2 %))
        (filter odd?)))
    (transduce xform + (range 0 10))
    
    它们的作用都是一样的。不同之处在于,您从不直接调用传感器,而是将它们传递给另一个函数。传感器知道该做什么,获取传感器的函数知道如何做。组合器的顺序就像您使用线程宏(自然顺序)编写它一样。现在您可以使用通道重新使用
    xform

    (chan 1 xform)
    

    传感器是减少功能的一种组合方式

    例如: 缩减函数是包含两个参数的函数:一个结果到目前为止和一个输入。它们返回一个新结果(到目前为止)。例如
    +
    :使用两个参数,可以将第一个参数视为到目前为止的结果,将第二个参数视为输入

    传感器现在可以接受+函数,并使其成为两倍+函数(在添加之前将每个输入加倍)。这就是传感器的外观(在最基本的术语中):

    如需说明,请将
    rfn
    替换为
    +
    ,以了解
    +
    如何转换为两次加号:

    (def twice-plus ;; result of (double +)
      (fn [r i] 
        (+ r (* 2 i))))
    
    (twice-plus 1 2)  ;-> 5
    (= (twice-plus 1 2) ((double +) 1 2)) ;-> true
    
    所以

    现在将产生12

    传感器返回的约化函数与结果的累积方式无关,因为它们与传递给它们的约化函数一起累积,而不知道是如何累积的。在这里,我们使用
    conj
    而不是
    +
    conj
    获取一个集合和一个值,并返回一个附加该值的新集合

    (reduce (double conj) [] [1 2 3]) 
    
    将产生[2 4 6]

    它们也与输入的来源无关

    可以将多个传感器链接为(可链接的)配方,以转换还原功能

    更新:因为现在有一个关于它的官方页面,我强烈建议阅读它:

    传感器是(据我所知!)函数,它接受一个还原函数并返回另一个还原函数。 还原函数是一个

    例如:

    user> (def my-transducer (comp count filter))
    #'user/my-transducer
    user> (my-transducer even? [0 1 2 3 4 5 6])
    4
    user> (my-transducer #(< 3 %) [0 1 2 3 4 5 6])
    3
    
    user>(def my传感器(补偿计数过滤器))
    #'用户/我的传感器
    用户>(我的传感器偶数?[0 1 2 3 4 5 6])
    4.
    用户>(我的传感器(<3%)[01 2 3 4 5 6])
    3.
    
    在这种情况下,我的传感器采用一个输入滤波函数,该函数应用于0,然后如果该值为偶数?在第一种情况下,滤波器将该值传递给计数器,然后过滤下一个值。而不是先进行滤波,然后将所有这些值传递给计数器

    在第二个例子中,它一次检查一个值,如果
    (reduce (double conj) [] [1 2 3]) 
    
    user> (def my-transducer (comp count filter))
    #'user/my-transducer
    user> (my-transducer even? [0 1 2 3 4 5 6])
    4
    user> (my-transducer #(< 3 %) [0 1 2 3 4 5 6])
    3
    
    Transducers are a powerful and composable way to build algorithmic transformations that you can reuse in many contexts, and they’re coming to Clojure core and core.async.
    
    ;; The Families in the Village
    
    (def village
      [{:home :north :family "smith" :name "sue" :age 37 :sex :f :role :parent}
       {:home :north :family "smith" :name "stan" :age 35 :sex :m :role :parent}
       {:home :north :family "smith" :name "simon" :age 7 :sex :m :role :child}
       {:home :north :family "smith" :name "sadie" :age 5 :sex :f :role :child}
    
       {:home :south :family "jones" :name "jill" :age 45 :sex :f :role :parent}
       {:home :south :family "jones" :name "jeff" :age 45 :sex :m :role :parent}
       {:home :south :family "jones" :name "jackie" :age 19 :sex :f :role :child}
       {:home :south :family "jones" :name "jason" :age 16 :sex :f :role :child}
       {:home :south :family "jones" :name "june" :age 14 :sex :f :role :child}
    
       {:home :west :family "brown" :name "billie" :age 55 :sex :f :role :parent}
       {:home :west :family "brown" :name "brian" :age 23 :sex :m :role :child}
       {:home :west :family "brown" :name "bettie" :age 29 :sex :f :role :child}
    
       {:home :east :family "williams" :name "walter" :age 23 :sex :m :role :parent}
       {:home :east :family "williams" :name "wanda" :age 3 :sex :f :role :child}])
    
    ;; Example 1a - using a reducer to add up all the mapped values
    
    (def ex1a-map-children-to-value-1 (r/map #(if (= :child (:role %)) 1 0)))
    
    (r/reduce + 0 (ex1a-map-children-to-value-1 village))
    ;;=>
    8
    
    ;; Example 1b - using a transducer to add up all the mapped values
    
    ;; create the transducers using the new arity for map that
    ;; takes just the function, no collection
    
    (def ex1b-map-children-to-value-1 (map #(if (= :child (:role %)) 1 0)))
    
    ;; now use transduce (c.f r/reduce) with the transducer to get the answer 
    (transduce ex1b-map-children-to-value-1 + 0 village)
    ;;=>
    8
    
    ;; Example 2a - using a reducer to count the children in the Brown family
    
    ;; create the reducer to select members of the Brown family
    (def ex2a-select-brown-family (r/filter #(= "brown" (string/lower-case (:family %)))))
    
    ;; compose a composite function to select the Brown family and map children to 1
    (def ex2a-count-brown-family-children (comp ex1a-map-children-to-value-1 ex2a-select-brown-family))
    
    ;; reduce to add up all the Brown children
    (r/reduce + 0 (ex2a-count-brown-family-children village))
    ;;=>
    2
    
    (transduce (filter #(not (.hasOwnProperty prevChildMapping %))) (.-push #js[]) #js [] nextKeys)