Clojure refactoring: flattening reduces
This morning I wrote some code which looked like:
(reduce (fn [acc x] (reduce (fn [acc y] (reduce f acc y)) acc x)) init xs)
(it was slightly more complex with some filtering and destructuring thrown in for good measure).
I wasn’t happy with those nested reduces and it occured to me that I could refactor it to use a single one:
(reduce f init (for [x xs, y x, z y] z))
Now that reads better!
OK, please keep this sort of thing to yourself. You’re scaring people. :) (see: http://search.twitter.com/search?q=too+stupid+to+program+in+clojure )
I’m kidding, but one of the reasons I love Clojure is that it’s deeply practical for problems that don’t resolve to math functions. Some elaboration might benefit this example. I guess you have a sequence of sequences of sequences and want to reduce f over all values contained therein? (where values are always in the innermost sequences only)
Thanks for the blog!
Took me a sec to figure out what you’re doing but that is freakin’ awesome. Will have to remember it in the future.
@Jeremey: it was a seq of maps of sets (which I could have obtained from a database) and the computation used values of the sets and keys of the maps.
That’s just awesome, really neat code!
I used to write something like this: (clojure.contrib.seq-utils/flatten (apply concat (map seq foo))) instead of (for [x foo, y x, z y] z), I don’t use ‘for’ enough.
[...] all the examples, you’re seeing the power of the seq-abstraction. Lets say you need to work on a nested strucuture, only working on the innermost data — double bound for is your friend. Imagine you have a [...]
Very neat way to flatten sequence of sequences! I would usually do something like what Nicolas did, but this way is much better. Thank you for posting it!
Thanks for writing this idiom up. I had a need for just such an idiom today and instead of a lot of small nested reduces I used the a for form to handle it all.
Same for me: just needed this and luckily remembered this post. You should rename your blog to “Treasure Chest” or so. :)