2013-02-04 32 views
5

我想做出点称为DS使如何在clojure中创建这个宏可变参数?

(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等等,但我不知道如何使它变参?

回答

9

在这里你去:

(defmacro ds [& symbols]                                
    `(clojure.string/join ", "                               
         ~(into [] 
          (map (fn [s] `(str ~(name s) "->" ~s)) symbols))))                 
+0

完美!谢谢! –

7

ANKUR的答案很可能是最实用的,但他推迟了很多工作,运行时可能在宏扩展的时间内完成的。这是一个很好的练习,并且电源宏的一个很好的示范可以带来,就看你有多少工作可以在编译时做到:

(defmacro ds [& args] 
    `(str ~(str (name (first args)) "->") 
     ~(first args) 
     [email protected](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) 

这避免了构建在运行时的任何临时对象,不绝对最少数量的字符串连接。

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"} 

弃用

我在离开这个作为什么做一个说明。

这里有一个变化,将在任何类型的表达式的工作(我认为)

(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 [email protected])] 
    (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 [email protected])] 
    (map clojure.pprint/pprint ss#) 
    (last vs#))) 

确实工作,我不知道为什么。它不打印输出,但宏观扩展看起来不错。可能是nREPL或REPL问题,但是我只是使用了上面的那个,不用担心括号太多。

+0

由于您使用了eval,因此它不适用于封闭。 eval不能访问词法环境,所以这是行不通的。令人高兴的是,如果不使用eval,就可以按照其他答案中描述的方式完成你正在做的事情。我给你留下与其他答案一样的技巧,以提供你想要的更一般的功能。 (我低估了这一点,因为使用'eval'这是非常错误的;如果你是在宏观时间做的话,我会很乐意转换为upvote)。 – amalloy

+0

哦,更仔细地阅读你的答案我看到我误解你说的话不起作用。我的第一个意思是'(让[x 1](vdump x))'由于范围确定而不起作用,但您可能没有尝试过。相反,您的代码无法打印,因为[地图很懒](https://stackoverflow.com/q/10857690/625403)。 – amalloy

+0

@amalloy哦,是的,我忘了懒惰的地图。 –

相关问题