Clojure中的make-keyword-map - 是否符合惯用法?

5

最近我开始写一些Clojure的代码,发现经常使用下面这种模式:

(let [x (bam)
      y (boom)]
  {:x x
   :y y})

于是我写了以下宏:

(defmacro make-keyword-map [& syms]
  `(hash-map ~@(mapcat (fn [s] [(keyword (name s)) s]) syms)))

有了这个,代码现在看起来像这样:

(let [x (bam)
      y (boom)]
  (make-keyword-map x y)

这种宏的写法是否被视为惯用的呢?还是我做错了什么,漏掉了已经建立的处理此类情况的模式?
1个回答

7

需要注意的是,你也可以将以下所有内容:

(let [x (bam) y (boom)] {:x x :y y})

改为:

{:x (bam) :y (boom)}

这样做会得到相同的结果。


如果你的let表达式相互依赖,那么可以使用如下宏:

(defmacro make-keyword-map [& let-body]
  (let [keywords-vals (flatten (map (juxt keyword identity)
                               (map first (partition 2 let-body))))]
    `(let ~(vec let-body)
       (hash-map ~@keywords-vals))))

这样 (make-keyword-map x (foo 1) y (bar 2) z (zoom x)) 就会被扩展为:

(clojure.core/let [x (foo 1) y (bar 2) z (zoom x)]
  (clojure.core/hash-map :x x :y y :z z))

那么类似以下这样的内容会起作用:

user=> (defn foo [x] (+ x 1))
#'user/foo
user=> (defn bar [x] (* x 2))
#'user/bar
user=> (defn zoom [x] [(* x 100) "zoom!"])
#'user/zoom
user=> (make-keyword-map x (foo 1) y (bar 2) z (zoom x))
{:z [200 "zoom!"], :y 4, :x 2}

不确定这是否符合惯用语,但与您原来的示例相比,它还可以节省一个let


这只是一个过于简化的例子。通常我需要使用这些本地绑定来生成其他本地绑定,例如 (let [x (bam) y (boom x) z (zoom x)] ...)。因为映射不能在自身内部引用其他键(因为它们在那个意义上不是“对象”)。 - missingfaktor
@missingfaktor 已经被添加到我的答案中。 - DJG
我认为这个新的 make-keyword-map 更加简洁。原始版本似乎在一个尴尬的抽象层次上运行,其中映射的定义与本地变量的名称交织在一起。 - Chuck
2
较短版本:(defmacro make-keyword-map [& bindings] (let [ks (take-nth 2 bindings)] \(let [~@bindings] (hash-map ~@(interleave (map keyword ks) ks)))))` - A. Webb

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接