In Clojure, It’s All (a lot of it at least) About Reduction

This blog and The Joy of Clojure are among many who talk about reduction being a major component in Clojure. They are right. Reduction is big stuff, especially coming from the imperative programming world.

I wrote this function:

(def accumail-url-keys ["CA", "STREET", "STREET2", "CITY", "STATE",

(defn ret-params
    "Generates all q-parameters and returns them in a vector of
    (let [param-vec [[]] ]
        (doseq [one-full-csv-row all-csv-rows]
            (let [accumail-csv-row one-full-csv-row
                  q-param (zipmap accumail-url-keys accumail-csv-row)
                  accu-q-param (first (rest (split-at 3 q-param)))
                  billing-param (first (split-at 3 q-param))]
                (conj param-vec accu-q-param billing-param)))

The goal of this function was to take parsed csv file rows from clojure-csv’s parse-csv function, and create a sequence of html tags and values that would be returned and used as :query-param values in multiple clj-http’s client/get calls.

But this function did not work, because param-vec is unmutable, having been initialized to an empty vector of vectors. I have made progress learning Clojure, but it has only been a few weeks programming in a functional programming language as opposed to years of empirical programming.

Fortunately for me and many others, there is the Clojure Google Group, and someone came up with using reduce. That did the trick. So, to make the transition from empirical to functional programming reduce is a gateway to dealing with sequences.

Now the function looks like this:

(defn ret-params
    "Generates all q-parameters and returns them in a vector of vectors."
      (fn [param-vec one-full-csv-row]
        (let [q-param (zipmap accumail-url-keys one-full-csv-row)
              accu-q-param (first (rest (split-at 3 q-param)))
              billing-param (first (split-at 3 q-param))]
          (conj param-vec [accu-q-param billing-param])))

and each arranged vector can then be put into map format for :query-params like so
(look for into further down in main):

(defn -main [& args]
  (with-command-line args
    "Get csv file name"
    [[file-name ".csv file name" "resultset.csv"]]
    [[file-name ".csv file name" 1]]
    (println "file-name:", file-name)
    (let [parsed-csv-output (gen-parsed-csv-file file-name)
          parsed-csv-vectors (ret-params parsed-csv-output)]
          (doseq [one-parsed-csv-vector parsed-csv-vectors]
            (println (into {} (first one-parsed-csv-vector)))))))

Of course, the println will be replaced with more function calls that take this data and write out another csv file suitable for a database upload.


6 Comments on “In Clojure, It’s All (a lot of it at least) About Reduction”

  1. I don’t see why reduce is needed here, a simple map will do as well. Compare

    (reduce (fn [acc x] (conj acc (* 2 x))) [1 2 3])


    (map (fn [x] (* 2 x)) [1 2 3]))

    • Octopusgrabbus says:


      Thanks for commenting.

      You are correct; reduce does not have to be used in this example.

      The map solution was another method suggested, when I posted my question in the Clojure group.

      • Of coure your solution is as correct as mine. I wanted to point out that map is the way to go for transformation every value of a sequence indepentently of the others. This is what your example is doing. An important detail is that map can be parallelized (pmap) while reduce, in general, cannot.

      • Octopusgrabbus says:

        This is good to know. Thanks.

  2. mc says:

    Did you mean “imperative” rather than “empirical”?

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s