I have some code that looks like this:
(let [radius 20
diameter (* 2 radius)
circumference (* pi diameter)]
{:radius radius
:diameter diameter
:circumference circumference})
I would like to simplify it to something like:
{:radius 20
:diameter (* 2 (% :radius))
:circumference (* pi (% :diameter))}
where % is the map itself.
Is this possible with the {}-syntax, or is there a macro to do this in
the standard library or contrib?
--
Timo
Am 31.08.2009 um 11:27 schrieb Timo Mihaljov:
> I have some code that looks like this:
>
> (let [radius 20
> diameter (* 2 radius)
> circumference (* pi diameter)]
> {:radius radius
> :diameter diameter
> :circumference circumference})
>
> I would like to simplify it to something like:
>
> {:radius 20
> :diameter (* 2 (% :radius))
> :circumference (* pi (% :diameter))}
>
> where % is the map itself.
You could define your own let-like construct for this:
(defmacro letmap [[m kvs & mkvs] & body]
(if m
`(let [~m {}
~@(mapcat (fn [[k v]] `(~m (assoc ~m ~k ~v))) kvs)]
(letmap ~mkvs ~@body))
`(do ~@body)))
Usage:
(letmap [a-map {:a 12
:b (* 2 (:a a-map))}
another-map {:c (:a a-map)
:d (+ (:b a-map) (:c another-map))}]
[a-map, another-map])
-> [{:b 24, :a 12} {:d 36, :c 12}]
Hope this helps.
Kind Regards,
achim
>> I would like to simplify it to something like:
>>
>> {:radius 20
>> :diameter (* 2 (% :radius))
>> :circumference (* pi (% :diameter))}
>>
>> where % is the map itself.
>
> You could define your own let-like construct for this:
>
>
> (defmacro letmap [[m kvs & mkvs] & body]
> (if m
> `(let [~m {}
> ~@(mapcat (fn [[k v]] `(~m (assoc ~m ~k ~v))) kvs)]
> (letmap ~mkvs ~@body))
> `(do ~@body)))
>
>
> Usage:
>
> (letmap [a-map {:a 12
> :b (* 2 (:a a-map))}
> another-map {:c (:a a-map)
> :d (+ (:b a-map) (:c another-map))}]
> [a-map, another-map])
>
> -> [{:b 24, :a 12} {:d 36, :c 12}]
Yeah, I needed something like this a while ago, and came up with this:
(defmacro let-map
"Equivalent of (let [a 5 b (+ a 5)] {:a a :b b})."
[kvs]
(let [keys (keys (apply hash-map kvs))
keyword-symbols (mapcat #(vector (keyword (str %)) %) keys)]
`(let [~@kvs]
(hash-map ~@keyword-symbols))))
user=> (let-map [a 5 b (inc a) c [a b] d {:foo c :bar (* a b)}])
{:a 5, :c [5 6], :b 6, :d {:foo [5 6], :bar 30}}
The nice thing about it is that you no longer need to use all the
keywords -- the definition of the map's slots becomes far more like
defining sequential bindings in a let.
- Chas
Thank you all for the answers!
I especially like Chas's solution. It didn't occur to me that the
redundancy can be eliminated by removing the map and leaving in the let
-- that's brilliant! :-) It's much easier to read because one doesn't
have to dig into the map to get to the variables, and it looks exactly
like the common idiom of interdependent let-bindings.
--
Timo