Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Clojure: See Every Step of a Reduce

DZone's Guide to

Clojure: See Every Step of a Reduce

· Java Zone ·
Free Resource

The CMS developers love. Open Source, API-first and Enterprise-grade. Try BloomReach CMS for free.

Last year I wrote about a Haskell function called scanl which returned the intermediate steps of a fold over a collection and last week I realised that I needed a similar function in Clojure to analyse a reduce I’d written.

A simple reduce which adds together the numbers 1-10 would look like this:

> (reduce + 0 (range 1 11))
55

If we want to see the intermediate values of this function called then instead of using reduce there’s a function called reductions which gives us exactly what we want:

> (reductions + 0 (range 1 11))
(0 1 3 6 10 15 21 28 36 45 55)

I found this function especially useful when analysing my implementation of the Glicko ranking algorithm to work out whether a team’s ranking was being updated correctly after a round of matches.

I initially thought the reductions function was only useful as a debugging tool and that you’d always end up changing your code back to use reduce after you’d solved the problem but I realise I was mistaken.

As part of my implementation of the Glicko algorithm I wrote a bit of code that applied a reduce across a collection of football seasons and initially just returned the final ranking of each team:

(def initial-team-rankings { "Man Utd" {:points 1200} "Man City" {:points 1300}})
 
(defn update-team-rankings [teams year]
  (reduce (fn [ts [team _]] (update-in ts [team :points] inc)) teams teams))
> (reduce update-team-rankings initial-team-rankings (range 2004 2013))
{"Man City" {:points 1309}, "Man Utd" {:points 1209}}

I realised it would actually be quite interesting to see the rankings after each season for which reductionscomes in quite handy.

For example if we want to find the rankings after 3 seasons we could write the following code:

> (nth (reductions update-team-rankings initial-team-rankings (range 2004 2013)) 3)
{"Man City" {:points 1303}, "Man Utd" {:points 1203}}

Or we could join the result back onto our collection of years and create a map so we can look up the year more easily:

(def final-rankings
  (zipmap (range 2003 2013) (reductions update-team-rankings initial-team-rankings (range 2004 2013))))
> (get final-rankings 2006)
{"Man City" {:points 1303}, "Man Utd" {:points 1203}}

BloomReach CMS: the API-first CMS of the future. Open-source & enterprise-grade. - As a Java developer, you will feel at home using Maven builds and your favorite IDE (e.g. Eclipse or IntelliJ) and continuous integration server (e.g. Jenkins). Manage your Java objects using Spring Framework, write your templates in JSP or Freemarker. Try for free.

Topics:

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}