3 ms·
I'm not quite sure what this means, so here's my attempt to translate this into Python. A reducer is a function such as `add`: def add(sum, num): ret
by vbit 12y ago
I'm not quite sure what this means, so here's my attempt to translate this into Python. A reducer is a function such as `add`:
def add(sum, num):
return sum + num
Of course you can plug `add` directly in `reduce(add, [1, 2, 3], 0)` which gives `6`.
A transducer is an object returned by a call such as `map(lambda x: x + 1)`.
You can now apply the transducer to a reducer and get another reducer.
map_inc = map(lambda x: x + 1)
add_inc = map_inc(add)
Our first reducer simply added, but the next one increments and then adds. We can use it as `reduce(add_inc, [1, 2, 3], 0)` which gives, I'm guessing, `9`.
Since the transducer returns a reducer as well, we can compose transducers:
r1 = filter(is_even)(map(increment)(add))
# use r1 in reduce()
It seems in clojure, reduce() isn't the only useful function that works with reducers, there are others which makes this all worthwhile.
Is my translation accurate?
- dopamean 12y agoI'm also hoping for an informative answer to this question. Anyone?
- spion 12y agoI think its just reduce, but in general its possible to write many implementations of reduce. It could be applied to lazy sequences, observables (to get a future or a new observable) and many other reducible things
- richhickey 12y agoYes, it's not just the 'reduce' function. You can think of many kinds of jobs in terms of seeded left reductions. Here's an example of some of the functions that can apply a transducer to their internal 'step' operation: https://gist.github.com/richhickey/b5aefa622180681e1c81 https://gist.github.com/richhickey/b5aefa622180681e1c81 Note how one transducer stack is created and can be reused in many different contexts.
- bcoates 12y agoIf I understand right (I may not): In Python, the (complex, generic) iter() protocol is how every container provides a method to iterate itself, and reduce is a trivial application of a reducer (like your add function) to a container by using the iter protocol. In Clojure, it's the opposite: there is a reduce() protocol and every reducible container knows how to reduce itself. the traditional reduce function just takes a reducer and a reducible and performs the reduce protocol on it. As there's a bunch of different kinds of containers with different rules, the best way to implement the reduce protocol on them is different for each, and there might be weird specialized reducers that are parallel or asynchronous or lazy or whatever. Transducers allow you to composibly transform reducers into different reducers, which can then be handed to the reduce protocol. As it turns out, most (all?) normal container->container operations, like map and filter, have corresponding transducers. Here's the good part: if you have a reduce protocol and a complete set of tranducers, containers don't need to be mappable. The map(fn, iterable) function needs to know how to iterate; mapping(fn) doesn't care about containers at all, and is fully general to mapping any reducible for free. So you can write a transducer to produce the effect of any map or filter-like operation without touching iter(). As an added bonus, the code is more efficient: reduce( add, map( lambda x: x + 1, xrange(10**9) ), 0 ) eagerly builds a gigantic mapped list (barring sophisticated laziness or loop-fusion optimizations), but reduce( mapping( lambda x: x + 1 )(add), xrange(10**9), 0 ) is equivalent and trivially runs in O(1) space on one element of xrange at a time. PS: python translation of mapping from http://clojure.com/blog/2012/05/15/anatomy-of-reducer.html http://clojure.com/blog/2012/05/15/anatomy-of-reducer.html converted to named functions to be more pythonic: def mapping( transformation ): def transducer( reducer ): def new_reducer( accum, next ): return reducer(accum, transformation(next) ) return new_reducer return transducer
- puredanger 12y agoExcellent answer.
- neotrinity 12y agomy heart somehow leaps when I see beautiful code like this. Although I have tried to like clojure in all earnestness, I am somehow put off by the noise from clojure ( PS its not only about the brackets. ) Somehow the python code and also the haskell version looks so succinct yet sparse enough to be read. maybe I am hardwired that way ...