Hello everyone,
Today I'd like to shed some light on a new funciton in 1.1, juxt.  In
order to understand juxt(apose), I'd like to first talk about comp
(ose).  Comp can be defined in terms of reduce like so:

(defn my-comp [& fns]
  (fn [args]
    (reduce
      (fn[accum f](f accum))
      (conj (reverse (seq fns)) args))))

Granted, this isn't 100% equivalent to the Clojure comp function, but
it is very, very close.  What it demonstrates is that comp applies a
list of functions in series using reduce.  After writing Clojure for a
while, one usually finds frequent need to apply a list of functions in
parallel using map.  juxt can be defined as follows

(defn juxt [& fns]
  (fn[ & arg] (map #(apply % args) fns)))

Notice that juxt creates a closure.  The most straightforward case is
to *predictably* access multiple values from a map.

user=>(def test-map {:a "1" :b "2" :c "3" :d "4"})

user=>((juxt :a :c) test-map)
("1" "3")

However, as one works with maps more and more, situations arise where
it is desired to perform many operations on a map at once.  For
example

;assume parse-int turns a string to an int appropriately
user=>((juxt :a (comp parse-int :c)) test-map)
("1" 3)

Since juxt returns a closure, it is very useful in any place one would
use a map operation as well.  For example, this can make turning a
list of maps into a list of lists very easy. Also, this made it very
easy to determine if a sub-selection of a hash-map is equal to another
hash- map

user=>(def test-juxt (juxt :a :c))
user=>(= (test-juxt {:a 1 :b 2 :c 3}) (test-juxt {:a 1 :b 34 :c 3}))
true

One thing that is very interesting is that this function allows one to
simulate the behavior of let in a point-free style.

;This is deliberate overkill for a small example
;Generate a list of squares
;Notice that the juxt fn uses the range twice
user=>((partial map (juxt identity #(* % %))) (range 1 6))
((1 1) (2 4) (3 9) (4 16) (5 25))

This also is useful when combined w/ clojure.contrib/group-by.
Suppose you have a sales database with a table in it.  This table keep
tracks of each sale (:id), when it happened (:year, :quarter), who
sold it (:sold-by), and what category (:category) the product was
in.

It is obviously useful to group items by who sold it, or what category
it was sold under.  However, it is also interesting to see which
employees are selling which items.  This is a grouping by :sold-by
AND :category.  In order to get at this information we'd do the
following.

;Assume our sales data is a list of maps, in the sales-coll variable
;returns a map with two element vectors as keys, a list of maps as the
vals.
user=>(group-by (juxt :sold-by :category) sales-coll)
<Lots-Of-Data>

Now, what happens when you need to change how you group the data?
Instead of :sold-by & :category, you need :year & :quarter?  juxt
makes it easy.

user=>(group-by (juxt :year :category) sales-coll)
<Lots-Of-Different-Data>

And finally, what happens when you need to break it down by all for
variables simultaneously?

user=>(group-by (juxt :year :category :sold-by :category) sales-coll)
<Our-Last-Data-Sample>

There are tons of other uses for juxt, and I would encourage you to
experiment and find out some of these uses yourself.  I hope these
examples help everyone understand how to use the new operator.

Happy Hacking,
Sean

-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en

Reply via email to