有人可以用简单的术语向我解释Clojure换能器吗?
我已经尝试阅读这个,但我仍然不明白他们的价值或他们取代。 他们是否使我的代码更短,更容易理解或什么?
更新
很多人都发布了答案,但是看到带有和不带有换能器的例子很简单,甚至像我这样的白痴都能理解。 当然,除非传感器需要一定程度的理解,在这种情况下,我永远不会理解它们:(
传感器是配方如何处理一系列数据,而不知道底层序列是什么(如何做)。 它可以是任何seq,asynchronous频道或可观察。
它们是可组合和多态的。
好处是,每次添加新数据源时,都不必实现所有标准组合器。 一次又一次。 因为用户可以在不同的数据源上重复使用这些配方,从而产生效果。
广告更新
在Clojure的早期版本1.7中,您有三种如何编写数据stream查询的方法:
- 嵌套调用
(reduce + (filter odd? (map #(+ 2 %) (range 0 10))))
- function组成
(def xform (comp (partial filter odd?) (partial map #(+ 2 %)))) (reduce + (xform (range 0 10)))
- 线程macros
(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))
他们都这样做。 不同之处在于,您从不直接呼叫传感器,而是将其传递给另一个function。 变送器知道该怎么做,变送器的function知道如何。 组合器的顺序就像你用线程macros(自然顺序)写的一样。 现在你可以重用通道的xform
:
(chan 1 xform)
传感器提高效率,并允许您以更模块化的方式编写高效的代码。
这是一个体面的运行 。
与组合对旧map
调用, filter
, reduce
等相比,您可以获得更好的性能,因为您不需要在每个步骤之间构build中间集合,并重复执行这些集合。
与reducers
相比,或者将所有操作手动组合为单个expression式,您可以更容易地使用抽象,更好的模块化和重用处理function。
换能器是减lessfunction的组合手段。
示例:还原函数是带有两个参数的函数:迄今为止的结果和一个input。 他们返回一个新的结果(到目前为止)。 例如+
:有两个参数,你可以把第一个作为结果,第二个作为input。
换能器现在可以使用+function,使其成为一个两倍以上的function(每增加一倍,每一个input)。 这就是换能器的外观(最基本的):
(defn double [rfn] (fn [ri] (rfn r (* 2 i))))
为了说明,用+
replacerfn
来看看+
如何转换成两倍以上:
(def twice-plus ;; result of (double +) (fn [ri] (+ r (* 2 i)))) (twice-plus 1 2) ;-> 5 (= (twice-plus 1 2) ((double +) 1 2)) ;-> true
所以
(reduce (double +) 0 [1 2 3])
现在会收益12。
减less换能器返回的function与结果如何积累无关,因为它们随着传递给它们的还原函数而积累,不知道如何。 这里我们使用conj
而不是+
。 Conj
需要一个集合和一个值,并返回一个附加了该值的新集合。
(reduce (double conj) [] [1 2 3])
会产生[2 4 6]
它们也独立于input的来源。
多个传感器可链接为(可链接)配方,以转换还原function。
更新:由于现在有一个关于它的官方网页,我强烈build议阅读它: http : //clojure.org/transducers
假设您想要使用一系列函数来转换数据stream。 Unix shell可以让你用pipe道运算符来做这种事情,例如
cat /etc/passwd | tr '[:lower:]' '[:upper:]' | cut -d: -f1| grep R| wc -l
(上面的命令用用户名中的大写字母或小写字母来统计字母r的用户数量)。 这是作为一组进程来实现的,每一个进程都从先前进程的输出中读取,所以有四个中间stream。 你可以想象一个不同的实现将五个命令组合成一个集合命令,它将从其input读取并且只输出一次。 如果中间产品价格昂贵,成分便宜,这可能是一个很好的折衷。
Clojure也是这样。 有多种简洁的方式来expression一个转换stream水线,但是根据您的操作方式,最终可能会有一个中间stream从一个函数传递到另一个函数。 如果你有很多数据,把这些函数组合成一个单一的函数会更快。 换能器可以很容易地做到这一点。 早期的Clojure创新,减less,让你也这样做,但有一些限制。 传感器消除了一些限制。
所以要回答你的问题,换能器不一定会让你的代码变得更短或者更容易理解,但是你的代码可能不会更长或者更less理解,如果你使用了大量的数据,换能器可以让你的代码更快。
这是一个相当不错的传感器概述。
我发现阅读来自换能器的例子-js帮助我在日常代码中如何使用它们的具体条款中了解它们。
例如,考虑这个例子(摘自上面链接的自述文件):
var t = require("transducers-js"); var map = t.map, filter = t.filter, comp = t.comp, into = t.into; var inc = function(n) { return n + 1; }; var isEven = function(n) { return n % 2 == 0; }; var xf = comp(map(inc), filter(isEven)); console.log(into([], xf, [0,1,2,3,4])); // [2,4]
首先,使用xf
看起来比Underscore通常的select更清洁。
_.filter(_.map([0, 1, 2, 3, 4], inc), isEven);
Rich Hickey在Strange Loop 2014会议(45分钟)上发表了一篇“传感器”的演讲。
他以简单的方式解释了传感器是什么,以及真实世界的例子 – 在机场处理袋子。 他清楚地区分了不同的方面,并与当前的方法进行了对比。 最后,他给出了他们存在的理由。
video: https : //www.youtube.com/watch?v = 6mTbuzafcII
传感器清晰的定义在这里:
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}])
那么我们想知道村里有多less个孩子呢? 我们可以很容易地find与以下减速机:
;; 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 (cf r/reduce) with the transducer to get the answer (transduce ex1b-map-children-to-value-1 + 0 village) ;;=> 8
另外,考虑到小组也是非常强大的。 例如,如果我们想知道布朗家庭有多less孩子,我们可以执行:
;; 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
我希望你能find有用的例子。 你可以在这里find更多
希望能帮助到你。
克莱门西奥·莫拉莱斯·卢卡斯。
换能器(以我的理解!)function,采取一个减lessfunction,并返回另一个。 减lessfunction是一个
例如:
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
在这种情况下,我的传感器采用一个input滤波函数,它适用于0,那么如果这个值是偶数? 在第一种情况下,filter将该值传递给计数器,然后过滤下一个值。 而不是首先筛选,然后通过所有这些值来计数。
在第二个例子中,它同样是一次检查一个值,如果该值小于3,则允许count加1。
我用一个clojurescript的例子来介绍这个,这个例子解释了如何通过replacereduce函数来扩展序列函数。
这是我读过的传感器的要点。 如果您考虑在map
, filter
等操作中进行硬编码的cons
或conj
操作,则还原function无法访问。
与传感器,减lessfunction是分离的,我可以取代它,就像我用原生javascriptarrayspush
感谢换能器。
(transduce (filter #(not (.hasOwnProperty prevChildMapping %))) (.-push #js[]) #js [] nextKeys)
filter
和朋友有一个新的元素操作,将返回一个传感function,您可以使用它来提供自己的减lessfunction。