Can I get a this variable in a def?
Is there a way to mimic a this
variable in something like (def foo {:two 2 :three (inc (:two this))})
? Even be开发者_如何学Gotter would be something like (def foo {:two 2 :three (inc ::two)})
. I was told that there is a library that does exactly this, but I can't really find anything similar.
Thanks!
If you want a temporary name for something, that's what let
is for.
(def foo (let [x {:two 2}]
(assoc x :three (inc (:two x)))))
I don't know of any library that does what you want. Every once in a while, someone suggests a "generalized arrow", like ->
but with a magic symbol you can stick in the intermediary expressions which will be replaced by something else. See for example here and here. But this idea tends to be shot down because it's more complex and confusing for little benefit. let
is your friend. See Rich's example:
(let [x []
x (conj x 1)
x (into x [2 3])
x (map inc x)]
...)
(Update: Rearranged & reworked. build-map
and (a sketch of) -m>
macros added.)
You could write this particular example as
(def foo (zipmap [:two :three] (iterate inc 2)))
The easiest general solution which occurs to me at this moment is
user> (-> {} (assoc :two 2) (#(assoc % :three (inc (:two %)))))
{:three 3, :two 2}
It's actually very flexible, although it does require you to write out assoc
repeatedly.
To enable syntax similar to that from the question text, you could use something like this:
(defn build-map* [& kvs]
(reduce (fn [m [k v]]
(assoc m k (v m)))
{}
kvs))
(defmacro build-map [& raw-kvs]
(assert (even? (count raw-kvs)))
(let [kvs (map (fn [[k v]] [k `(fn [m#] (let [~'this m#] ~v))])
(partition 2 raw-kvs))]
`(build-map* ~@kvs)))
user> (build-map :two 2 :three (inc (:two this)))
{:three 3, :two 2}
You could easily change this to use a user-supplied symbol rather than the hardcoded this
. Or you could switch to %
, which is just a regular symbol outside anonymous function literals. Maybe add an explicit initial map argument, call it -m>
(for map threading) and you can do
(-m> {} :two 2 :three (inc (:two %)))
for the same result.
Another funky way (mostly for the fun):
;;; from Alex Osborne's debug-repl,
;;; see http://gist.github.com/252421
;;; now changed to use &env
(defmacro local-bindings
"Produces a map of the names of local bindings to their values."
[]
(let [symbols (map key &env)]
(zipmap (map (fn [sym] `(quote ~sym)) symbols) symbols)))
(let [two 2
three (inc two)]
(into {} (map (fn [[k v]] [(keyword k) v]) (local-bindings))))
{:two 2, :three 3}
Note that this will also capture the bindings introduced by any outer let forms...
精彩评论