Object Commando languages, development and design

14Sep/100

Clojure 1.2 New Functions

Version 1.2 of Clojure was released not too long ago with lots of new features. Things like protocols, a metadata reader macro change etc are some of the bigger differences (nice slides on these big changes can be found here). In addition to these bigger changes, there are also a lot of very useful new functions added to Clojure core. Below are some of the new ones that I have found useful along with some info about them. Note that most of these existed in contrib before moving to core.

group-by

The group-by function takes a function as a first argument and a collection as the second:

(doc group-by)
-------------------------
clojure.core/group-by
([f coll])
  Returns a map of the elements of coll keyed by the result of
  f on each element. The value at each key will be a vector of the
  corresponding elements, in the order they appeared in coll.

A basic example of this would be to group a list of numbers by those that are odd and those that are even.

(group-by odd? (range 1 10))
=> {true [1 3 5 7 9], false [2 4 6 8]}

The above code calls odd? on each item from (range 1 10) and then puts the number in the true or false slot of the map, depending on whether or not it's odd. The important part here is that the function that is passed in could return anything, not just true and false. Similar to the above code, we could create groups with the string "odd" and "even"

(group-by #(if (odd? %) "odd" "even") (range 1 10))
=> {"odd" [1 3 5 7 9], "even" [2 4 6 8]}

Maybe we have a list of address data structures, which is just a list of the street, followed by the city, then state. We could group by city with a call like:

(def addresses (list '("742 Evergreen" "Springfield" "MO")
                             '("31 Spooner Street" "Quahog" "RI")
                             '("742 Evergreen" "Springfield" "VT")
                             '("742 Evergreen" "Springfield" "NT")))

(group-by second addresses)
=> {"Springfield" [("742 Evergreen" "Springfield" "MO")
                          ("742 Evergreen" "Springfield" "VT")
                          ("742 Evergreen" "Springfield" "NT")],
     "Quahog" [("31 Spooner Street" "Quahog" "RI")]}

The usage with the address like above is very similar to how I have used it.

shuffle

shuffle is a basic function that pseudo-randomly rearranges the elements of a collection using the basic java.util.Collections shuffle method.

(doc shuffle)
-------------------------
clojure.core/shuffle
([coll])
  Return a random permutation of coll

The usage of it is pretty intuitive:

(shuffle (range 1 10))
=> [6 8 1 2 4 3 7 9 5]

Pretty basic, but saved me some additional work.

reductions

reductions is an interesting function in that it's kind of a mingling between the map function and the reduce function. It's lazy like map, but you pass in an accumulator like in reduce. It outputs a list of the intermediate accumulator values. As an example

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

What I think is the most useful aspect of this is that it can maintain a lazy flow. So we can swap the 10 number range above with an infinite sequence

(def natural-numbers (iterate inc 1))
(take 100 (reductions + 0 natural-numbers))
=> (0 1 3 6 10 15 21 28 36 45 55 66 78...)

One gotcha is the first element in the returned list. In the above two results, the initial value (0) was specific in the function call and it is also the first item returned in the result list.

get-in

This is a useful function for getting items out of nested maps. Let's say we have a nested map that has a single letter as a key in the first map. The value at those keys are a map keyed by a two character key which has a value of a map with a three character key etc. To get the nested value of the third map, we could use get-in like below;

(def x {:a {:ab {:abc "123"}}, :b {:bc {:bcd "234"}}})
(get-in x [:a :ab :abc])
=> "123"

Thanks to Nate for showing me this function, I've since used it several times. Similar to this function is the assoc-in and the update-in function. They are similar in that they operate on nested maps, but they modify the nested map, rather than retrieve a value. The documentation for get-in is below:

(doc get-in)
-------------------------
clojure.core/get-in
([m ks] [m ks not-found])
Returns the value in a nested associative structure,
where ks is a sequence of ke(ys. Returns nil if the key is not present,
or the not-found value if supplied.

spit

spit is a very convenient function for writing the contents of a string to a file. Here's the docs for the function

(doc spit)
-------------------------
clojure.core/spit
([f content & options])
  Opposite of slurp.  Opens f with writer, writes content, then
  closes f. Options passed to clojure.java.io/writer.

Pretty self explainatory. You don't need to worry about opening, flushing or closing a stream. Below is code that takes a string named info and outputs it to a file:

(def info "some info to be written to a file...")
(spit "/path/to/file/info.txt" info)