I want to define a macro that randomly chooses one of the given expressions and evaluates it. For example,
(equal-chance
(println "1")
(println "2"))
should print "1" half the time and "2" the other half.
I tried using,
(defmacro equal-chance
[& exprs]
`(rand-nth '~exprs))
but this returns one of the quoted forms, rather than evaluating it (i.e. it would return (println "1")
rather than actually printing "1"). I cannot use eval
because it does not preserve the bindings. For example,
(let [x 10] (eval '(println x)))
complains that it is unable to resolve symbol x.
Is there a way to evaluate a quoted form in the local scope? Or maybe there is a better way to go about this?
You can't evaluate a run-time value in a lexical environment that only exists at compile time. The solution is to use fn
instead of quote
and a function call instead of eval
:
(defmacro equal-chance [& exprs]
`((rand-nth [~@(map (fn [e] `(fn [] ~e)) exprs)])))
The way this works is by expanding (equal-chance e1 e2 ...)
into ((rand-nth [(fn [] e1) (fn [] e2) ...]))
.
Just don't quote the call to rand-nth
or the expressions. This will do what you want:
(defmacro equal-chance
[& exprs]
(rand-nth exprs))
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With