Sean Devlin
unread,Aug 22, 2009, 2:24:43 PM8/22/09Sign in to reply to author
Sign in to forward
You do not have permission to delete messages in this group
Either email addresses are anonymous for this group or you need the view member email addresses permission to view the original message
to Clojure Dev
Well, as promised here is round two of my proposed additions to
contrib. Today's thread is about the map-utils namespace.
Higher Order Functions
All the higher order functions in clojure accept and return a seq. It
is common to transform the resulting seq into a hash map.
These are a few functions that do this for you automatically.
Let's create a map for example purposes.
user=> (def abc123 {"a" 1 "b" 2 "c" 3})
#'user/abc123
* map-vals
This is like the `map` operator, but it applies `f` to every value of
the hash map instead of the entry. It returns a hash map.
user=> (map-vals #(* 2 %) abc123)
{"c" 6, "b" 4, "a" 2}
* map-keys
This is like the `map` operator, but it applies `f` to every key of
the hash map instead of the entry. It returns a hash map.
user=> (map-keys keyword abc123)
{:c 3, :b 2, :a 1}
* filter-map
This behaves just like `filter`. `pred` is applied to each entry of
the hash-map, and the resulting collection is transformed into a hash
map.
user=> (filter-map (comp even? second) abc123)
{"b" 2}
* remove-map
This behaves just like `remove`. `pred` is applied to each entry of
the hash-map, and the resulting collection is transformed into a hash
map.
user=> (remove-map (comp even? second) abc123)
{"a" 1, "c" 3}
#Tranforming a map
This section explores the `trans` closure, which is used to modify a
map.
* trans
I defined a function trans
(defn trans [& params]...)
Let me show an example:
user=> ((trans :count count) abc123)
{:count 3, "a" 1, "b" 2, "c" 3}
Notice the call to trans first, and then the result it applied to
test-
map. This is because trans generates a closure. In this case, it
applies the count function to the map, and associates it with the
key :count.
user=> ((trans "a" (comp inc #(get % "a"))) abc123)
{:a 1, :b "B", :c "C"}
* deftrans
trans is a little cumbersome, generating a closure. I also wrote a
deftrans macro. It creates a trans and stores it in the provided
name:
user=> (deftrans counter :count count)
#'user/counter
user=> (counter abc123)
{:count 3, "a" 1, "b" 2, "c" 3}
user=> (deftrans inc-a :a (comp inc :a))
#'user/inc-a
user=> (inc-a abc123)
{:a 1, :b "B", :c "C"}
* Using a closure
Let's revisit the fact that trans generates a closure. We can use
the
resulting transform anywhere we'd use a function.
### In a map
user=> (map counter (repeat 5 abc123))
({:count 3, "a" 1, "b" 2, "c" 3}
{:count 3, "a" 1, "b" 2, "c" 3}
{:count 3, "a" 1, "b" 2, "c" 3}
{:count 3, "a" 1, "b" 2, "c" 3}
{:count 3, "a" 1, "b" 2, "c" 3})
### In a comp
user=> ((comp counter counter) abc123)
{:count 4, "a" 1, "b" 2, "c" 3}
### In the STM
This is my favorite use of trans so far
user=> (def test-ref (ref abc123))
#'user/test-ref
user=> (dosync (alter test-ref counter))
{:count 3, "a" 1, "b" 2, "c" 3}
user=> @test-ref
{:count 3, "a" 1, "b" 2, "c" 3}
* trans*
The trans function associates its values after all the functions have
been evaluated
user=> ((trans :c1 count :c2 count :c3 count) abc123)
{:c3 3, :c2 3, :c1 3, "a" 1, "b" 2, "c" 3}
I have also defined the trans* closure, which associates the value in
the map between each iteration. I believe
this mimic the distinction between let & let* in CL, but I am unsure.
user=> ((trans* :c1 count :c2 count :c3 count) abc123)
{:c3 5, :c2 4, :c1 3, "a" 1, "b" 2, "c" 3}
That completes today's suggestions. Any thoughts?
Sean