Skip to content

Instantly share code, notes, and snippets.

@cemerick
Last active April 27, 2017 13:39
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cemerick/6803d53a541d68e7303d68587e952261 to your computer and use it in GitHub Desktop.
Save cemerick/6803d53a541d68e7303d68587e952261 to your computer and use it in GitHub Desktop.
reduce-for: easier-to-understand nested reductions
; a toy example of a nested reduction; especially when function bodies are nontrivial, or when the nesting
; gets much deeper than 2 levels, it's easy to lose track of which values are being reduced over at each
; level, etc
(let [x {:a [1 2 3] :b [4 5 6]}]
(reduce
(fn [x [k vs]]
(reduce
(fn [x v] (update x :sum (fnil + 0) v))
x
(map inc vs))
x
x))
=> {:a [1 2 3], :b [4 5 6], :sum 27}
(defmacro reduce-for [v steps & body]
(if (empty? steps)
`(do ~@body)
`(reduce
(fn [~v ~(first steps)]
(reduce-for ~v ~(drop 2 steps) ~@body))
~v
~(second steps))))
=> #'user/reduce-for
; reduce-for provides a for-like syntax for defining each step of the nested reduction,
; so they're all kept together. The `body` of the `reduce-for` becomes the innermost
; reduce's body
(let [x {:a [1 2 3] :b [4 5 6]}]
(reduce-for x [[k vs] x
v (map inc vs)]
(update x :sum (fnil + 0) v)))
=> {:a [1 2 3], :b [4 5 6], :sum 27}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment