I am going to discuss a function I think belongs in the Clojure core language. The function is called transpose. It is defined as follows.
(defn transpose [xs] (apply map vector xs))
Here is what it does:
user> (def m [ ['a 'b 'c 'd] [1 2 3 4] ]) #'user/m user> m [[a b c d] [1 2 3 4]] user> (transpose m) ([a 1] [b 2] [d 4]) user> (transpose (transpose m)) ([a b c d] [1 2 3 4])
Frequently we are presented with data physically organized as rows or records. These rows may be semantically meaningful, but even so, we often want to consider the same data as columns. The transpose functions allows us to easily move between the row and column views of the data. Here’s a typical example.
Suppose we took measurements of some test subjects. For each subject we record height in inches, weight in pounds, age in years. The data looks like this:
user> (def data [ [60.0 180.1 22] [62.0 197.4 19.1] [58.8 166.6 29.1] [59 174 31] ])
It is typical to want to compute the sample means for the data. No problem:
user> (map mean (transpose data))
(59.95 179.525 25.3)
Here's another use case. We have several functions f,g,h etc. We want a way to apply all the functions to the same argument x, and return the functions in the order corresponding to (sort (f x) (g x) ...). The problem is a bit contrived, but will serve to illustrate what I think of as the TST pattern: transpose, sort, transpose. Here's the code.
(defn f [x] x) ;; set up test functions
(defn g [x] 3)
(defn h [x] (* x x))
(defn i [x] (* 2 (- x)))
(defn vals [funcs x]
(for [f funcs] (f x)))
user> (vals [f g h i] 13)
(13 3 169 -26)
With that machinery in place, here's the code showing the transpose-sort-transpose pattern.
(defn fsort [funcs x]
(let [v (vals funcs x)]
(->> [v funcs] ;;line A
This will return the functions in the desired order. Here's why. Look at the [v funcs] pair in line A. It looks like
[ [(f x) (g x) (h x) (i x)] [f g h i] ]
Running that through the first transpose results in
[ [(f x) f ] [(g x) g] [(h x) h] [(i x) i] ]
Then that is sorted on the first coordinate, reordering it, say to
[ [(g x) g ] [(i x) i] [(h x) h] [(f x) f] ]
Now the functions are in the order we want. The second transpose decouples the functions and values, resulting in
[ [(g x) (i x) (h x) (f x)] [g i h f] ]
Running this result through second leaves us with just the functions in the desired order. If you are of a mind to test this, here's one way
(defn in-order? [funcs x]
(apply <= (vals (fsort funcs x) x)))
user> (every? true? (map (partial in-order? [f g h i]) (range 50000)))
I have found the transpose-sort-transpose pattern pops up with surprising regularity. But a pattern pops up in part because we have reified it by giving it a name, and so can recognize it. Giving the transpose function a name makes it much easier to see uses for the TST pattern.
The transpose function is not something I made up. It has a long tradition in linear algebra, where it is called transpose. I hope the transpose function can make the jump into Clojure core. In the meantime, I'll just keep its definition loaded as a yasnippit shortcut.