我在let块中做了很多计算,返回包含数据的哈希映射.以下是一个不那么简单的例子:
(def ground-truth
(let [n 201
t1 2.0
linspace (fn [a b n] (let [d (/ (- b a) (dec n))]
(map (fn [x] (+ a (* x d))) (range n))))
times (vec (linspace 0.0, t1, n))
wavelength 1
wavespeed 1
f (* (/ wavespeed wavelength) 2 Math/PI)
dt (- (get times 1) (get times 0))
amplitude 5.0
ground-level 10.0
h-true (mapv #(+ ground-level
(* amplitude (Math/sin (* f %))))
times)
h-dot-true (mapv #(* amplitude f (Math/cos (* f %)))
times)
baro-bias-true -3.777]
{:n n, :times times, :f f, :dt dt, :h-true h-true,
:h-dot-true h-dot-true, :baro-bias-true baro-bias-true}))
Run Code Online (Sandbox Code Playgroud)
我想要做的是摆脱最终表达中的重复.对于这个小例子来说,这不是一个大问题,但我有一些更长,更复杂,重复使得修改表达式变得乏味且容易出错.
我试过这个宏:
(defmacro hashup [name-list]
`(into {}
(map vector
(mapv keyword ~name-list)
(mapv eval ~name-list))))
Run Code Online (Sandbox Code Playgroud)
仅eval适用于vars以下情况的作品:
(def foo 41) (def bar 42)
(hashup '[foo bar])
Run Code Online (Sandbox Code Playgroud)
{:foo 41,:bar 42}
但不是let块:
(let [a 1, b (inc a)] (hashup '[a b]))
Run Code Online (Sandbox Code Playgroud)
CompilerException java.lang.RuntimeException:无法解析符号:a在此上下文中,编译:(null:1:1)Util.java:221 clojure.lang.Util/runtimeException
core.clj:3105 clojure.core $ eval/invokeStatic
在审查了以下SO问题之后,正如预期的那样:Clojure中的变量范围+评估,eval列出了一个允许的clojure
有人可能会说"好吧,你可以let通过def在命名空间中使用变量来重复你的块之外的重复,然后使用类似的东西hashup,或者你可以在let块的底部重复你的操作并忘记宏观魔法.但是没有办法在这个确切的用例中不要重复自己.
我是否想念一种干掉这种代码的好方法?
试试这个:
(defmacro ->hash [& vars]
(list `zipmap
(mapv keyword vars)
(vec vars)))
Run Code Online (Sandbox Code Playgroud)
然后:
(->hash a b c) => {:a a :b b :c c}
Run Code Online (Sandbox Code Playgroud)
它也适用于let块内.