我想做一些叫做ds的东西
(let [a 2]
(ds a))
- >
"a->2"
和
(let [a 1 b 2 c 3]
(ds a b c))
- >
"a->1, b->2, c->3"
到目前为止,我已经达到了目标:
(defmacro ds3 [a b c]
`(clojure.string/join ", "
[(str '~a "->" ~a)
(str '~b "->" ~b)
(str '~c "->" ~c)]))
这似乎有效:
(let [ a 1 b 2 c 3]
(ds3 a b c)) ; "1->1, 2->2, 3->3"
显然我可以定义ds1 ds2 ds3等...但是我想知道如何使它成为可变参数?
答案 0 :(得分:10)
你走了:
(defmacro ds [& symbols]
`(clojure.string/join ", "
~(into []
(map (fn [s] `(str ~(name s) "->" ~s)) symbols))))
答案 1 :(得分:7)
Ankur的答案可能是最实用的,但他将很多工作推迟到运行时,这可以在宏扩展时完成。这是一个非常有用的练习,并且可以很好地演示功能宏,看看你在编译时可以完成多少工作:
(defmacro ds [& args]
`(str ~(str (name (first args)) "->")
~(first args)
~@(for [arg (rest args)
clause [(str ", " (name arg) "->") arg]]
clause)))
(macroexpand-1 '(ds a b c))
=> (clojure.core/str "a->" a ", b->" b ", c->" c)
这可以避免在运行时构建任何临时对象,并且可以使用绝对最少的字符串连接数。
答案 2 :(得分:1)
修改强>:
感谢@amalloy的建议,这里有一些改进的宏,不使用'严重错误'eval
并包含一些小测试:
(import 'java.lang.ArithmeticException)
(defmacro explain-expr
"Produce a string representation of the unevaluated expression x, concatenated to
an arrow and a string representation of the result of evaluating x, including
Exceptions should they arise."
[x]
`(str ~(str x) " ~~> "
(try ~x (catch Exception e# (str e#)))))
(println (explain-expr (* 42 42)))
(println (explain-expr (let [x 1] x)))
(println (explain-expr (/ 6 0)))
(println (let [x 1] (explain-expr x)))
(let [y 37] (println (explain-expr (let [x 19] (* x y)))))
(let [y 37] (println (explain-expr (let [y 19] (* y y)))))
(* 42 42) ~~> 1764 (let [x 1] x) ~~> 1 (/ 6 0) ~~> java.lang.ArithmeticException: Divide by zero x ~~> 1 (let [x 19] (* x y)) ~~> 703 (let [y 19] (* y y)) ~~> 361
(defmacro explain-exprs
"Produce string representations of the unevaluated expressions xs, concatenated
to arrows and string representations of the results of evaluating each
expression, including Exceptions should they arise."
[& xs]
(into [] (map (fn [x]
`(str ~(str x) " ~~> "
(try ~x (catch Exception e# (str e#)))))
xs)))
(clojure.pprint/pprint
(let [y 37]
(explain-exprs
(* 42 42)
(let [x 19] (* x y))
(let [y 19] (* y y))
(* y y)
(/ 6 0))))
["(* 42 42) ~~> 1764" "(let [x 19] (* x y)) ~~> 703" "(let [y 19] (* y y)) ~~> 361" "(* y y) ~~> 1369" "(/ 6 0) ~~> java.lang.ArithmeticException: Divide by zero"]
(defmacro explanation-map
"Produce a hashmap from string representations of the unevaluated expressions
exprs to the results of evaluating each expression in exprs, including
Exceptions should they arise."
[& exprs]
(into {}
(map (fn [expr]
`[~(str expr)
(try ~expr (catch Exception e# (str e#)))])
exprs)))
(clojure.pprint/pprint
(let [y 37]
(explanation-map
(* 42 42)
(let [x 19] (* x y))
(let [y 19] (* y y))
(* y y)
(/ 6 0))))
{"(* 42 42)" 1764, "(let [x 19] (* x y))" 703, "(let [y 19] (* y y))" 361, "(* y y)" 1369, "(/ 6 0)" "java.lang.ArithmeticException: Divide by zero"}
<强> DEPRECATED 强>:
我将此作为不要做的事情的说明。
这是一种适用于任何表达方式的变体(我认为)
(defmacro dump-strings-and-values
"Produces parallel vectors of printable dump strings and values. A dump string
shows an expression, unevaluated, then a funny arrow, then the value of the
expression."
[& xs]
`(apply map vector ;; transpose
(for [x# '~xs
v# [(try (eval x#) (catch Exception e# (str e#)))]]
[(str x# " ~~> " v#) v#])))
(defmacro pdump
"Print dump strings for one or more given expressions by side effect; return
the value of the last actual argument."
[& xs]
`(let [[ss# vs#]
(dump-strings-and-values ~@xs)]
(clojure.pprint/pprint ss#)
(last vs#))
一些样本:
(pdump (* 6 7))
打印["(* 6 7) ~~> 42"]
并返回42
。
(pdump (* 7 6) (/ 1 0) (into {} [[:a 1]]))
打印
["(* 7 6) ~~> 42"
"(/ 1 0) ~~> java.lang.ArithmeticException: Divide by zero"
"(into {} [[:a 1]]) ~~> {:a 1}"]
并返回{:a 1}
。
修改强>:
我试图摆脱打印输出中的外括号,即
(defmacro vdump
"Print dump strings for one or more given expressions by side effect; return
the value of the last actual argument."
[& xs]
`(let [[ss# vs#]
(dump-strings-and-values ~@xs)]
(map clojure.pprint/pprint ss#)
(last vs#)))
不工作,我不知道为什么。它不打印输出,但宏扩展看起来不错。可能是一个nREPL或REPL问题,但我放弃了,只是使用上面的那个,不要太担心括号。