Clojure:将变量的值传递给宏

时间:2019-07-29 13:03:17

标签: vector clojure hashmap macros

我想将{a 1, b 2} clojure.lang.PersistentArrayMap 转换为 [a 1 b 2] Clojure中的 clojure.lang.PersistentVector

我试图用clojure编写一个将{a 1,b 2}转换为[a 1 b 2]的函数。我还写了一个宏,可以给我预期的最终结果。在Clojure中,我们无法将函数内部生成的值传递给宏。为此,我想知道一种可以直接实现宏的方法,该宏可以将{a 1,b 2}转换为(let [a 1 b 2](println a)),该宏将返回1。

虚拟宏:

(defmacro mymacro [binding & body]
--some implemetation---)

执行:

(mymacro '{a 1, b 2} (println a))

输出:

1
nil

我的实现:

转换为所需输出的功能。

(defn myfn [x]
 (let [a (into (vector) x) b (vec (mapcat vec a))]  b))

执行:

(myfn '{a 1, b 2})

输出:

[a 1 b 2]

宏:

(defmacro list-let [bindings & body] `(let ~(vec bindings) ~@body))

执行:

(list-let [a 1 b 2] (println a))

输出:

1
nil

我想知道如何在宏本身内部实现相同的功能,并避免使用函数实现来获取require输出。与上面给出的虚拟宏相同。我也很想知道是否可以通过任何方式将函数中的值传递给宏,而无需使用     (def)

2 个答案:

答案 0 :(得分:3)

通常,宏代码是普通的clojure代码(不同之处在于它返回的clojure代码将在以后进行评估)。因此,您几乎可以想到在clojure中进行编码的任何事情,都可以在宏内部对传递的参数进行处理。

例如,这是您要尝试做的事情(如果我理解正确的话):

(defmacro map-let [binding-map & body]
  (let [b-vec (reduce into [] binding-map)]
    `(let ~b-vec ~@body)))

(map-let {a 10 b 20} 
  (println a b) 
  (+ a b))
;;=> 10 20
30

或类似这样:

(defmacro map-let [binding-map & body]
  `(let ~(reduce into [] binding-map) ~@body))

甚至是这样:

(defmacro map-let [binding-map & body]
  `(let [~@(apply concat binding-map)] ~@body))

答案 1 :(得分:1)

您不需要为此使用宏,并且在可能的情况下,始终应首选函数而不是宏。

对于您的特殊情况,我有already written a function keyvals,您可能会觉得很方便:

(keyvals m)
 "For any map m, returns the keys & values of m as a vector,
  suitable for reconstructing via (apply hash-map (keyvals m))."

(keyvals {:a 1 :b 2})
;=> [:b 2 :a 1]
(apply hash-map (keyvals {:a 1 :b 2}))
;=> {:b 2, :a 1}

还有the full API docs


如果您对实现感到好奇,这很简单:

(s/defn keyvals :- [s/Any]
  "For any map m, returns the (alternating) keys & values of m as a vector, suitable for reconstructing m via
   (apply hash-map (keyvals m)). (keyvals {:a 1 :b 2} => [:a 1 :b 2] "
  [m :- tsk/Map ]
  (reduce into [] (seq m)))