2013-02-04 51 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哦,是的,我忘了懶惰的地圖。 –

相關問題