哈希映射的Clojure DRY模式?

我在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}))

我想要做的是摆脱最终表达中的重复.对于这个小例子来说,这不是一个大问题,但我有一些更长,更复杂,重复使得修改表达式变得乏味且容易出错.

我试过这个宏:

(defmacro hashup [name-list]
`(into {}
        (map vector
             (mapv keyword ~name-list)
             (mapv eval    ~name-list))))

只有当eval工作时才有效,它适用于变量:

(def foo 41) (def bar 42)
(hashup '[foo bar])

{:foo 41, :bar 42}

但不是让let块:

(let [a 1, b (inc a)] (hashup '[a b]))

CompilerException java.lang.RuntimeException: Unable to resolve symbol: a in this context, compiling:(null:1:1)
Util.java: 221 clojure.lang.Util/runtimeException
core.clj: 3105 clojure.core$eval/invokeStatic

正如在审查以下SO问题之后所预期的那样:Variable scope + eval in Clojure,eval a list into a let on clojure

有人可能会说“好吧,你可以通过在命名空间中对变量进行deffing然后使用hashup之类的东西来重复你的let块之外的重复,或者你可以在let块的底部重复你的东西而忘记宏魔法.但是有没有办法在这个确切的用例中不要重复自己.

我是否想念一种干掉这种代码的好方法?

最佳答案
试试这个:

(defmacro ->hash [& vars]
  (list `zipmap
    (mapv keyword vars)
    (vec vars)))

然后:

(->hash a b c) => {:a a :b b :c c}

它也适用于let块.

转载注明原文:哈希映射的Clojure DRY模式? - 代码日志