2013-02-26 81 views
1

我想定義一個函數,將創建一個宏,並在我試圖動態提供宏的名稱時遇到問題。這裏列出了我正在面臨的問題的縮小代碼:功能,創建宏作爲參數提供的宏名稱

(defn create-times-macro [n] 
    (defmacro thatManyTimes [a] 
    `(* ~n ~a))) 

(create-times-macro 2) 

(thatManyTimes 3) ;; evals to 6 

到目前爲止這麼好。現在說我要供應宏的名稱作爲參數:

(defn create-times-macro [macroName n] 
    (defmacro macroName [a] 
    `(* ~n ~a))) 

(create-times-macro (symbol "multiplyBy") 3) 
(multiplyBy 3) ;; fails with unable to resolve symbol multiplyBy 
(create-times-macro "multiplyBy" 3) 
(multiplyBy 3) ;; same failure 

回答

1

不知道這是做的最好的方式,但它的工作原理:

=> (defmacro create-times-macro [macroName n] 
    (let [the-name (symbol macroName)] 
     `(defmacro ~the-name [a#] 
      (* ~n a#)))) 
#'user/create-times-macro 
=> (create-times-macro "hi" 3) 
#'user/hi 
=> (hi 4) 
12 
+0

你確定它能在JAR中工作嗎?請參閱下面的@Ankur答案。 – Blacksad 2013-02-26 23:04:02

+0

@Blacksad:這將在JAR中工作,因爲它是宏創建宏而不是創建宏的函數。 – Ankur 2013-02-27 04:25:52

+0

我明白了。感謝您的領導! – Blacksad 2013-02-27 05:18:26

1

你不需要宏這個案例。函數在大多數情況下都適用,並且它們比宏更有能力。例如,如果您創建爲功能

(defn create-times-fn [n] 
    (fn [a] 
    (* n a))) 

,你會得到相同的結果

(def three-times (create-times-fn 3)) 

(three-times 2) 
=> 6 

(let [three-times (create-times-fn 3)] 
    (three-times 2)) 

而且你可以把它作爲參數傳遞給其他函數

(map (create-times-fn 2) (range 5)) 
=> (0 2 4 6 8) 

或退貨作爲結果或與其他功能組合。你用宏丟失了所有這些東西。

+0

這是一個縮小的例子。這不是我正面臨的實際情況。我只是想出了一個簡單的例子來說明我正在嘗試做什麼。 – 2013-02-26 18:00:59

+0

對不起。我只是看不到創建另一個宏的宏的目的。 – mobyte 2013-02-27 02:55:57

4

看來你對使用宏感到困惑。

  • 其中創建一個功能宏 - 行
  • 其中創建另一個宏宏 - 行
  • 函數創建宏 - 不OK(不常見和通常)。

所有這些觀點都表明,宏是編譯時的事情,其中​​函數是運行時的事情。通常的方向是編譯時間到運行時間,最後一點是從運行時間到編譯時間。

你的第一個代碼示例在REPL中有效,但它在編譯的JAR中不起作用。在REPL中,您處於編譯時 - >運行 - >打印 - >循環,因此您可以從循環中返回編譯 - >時間,這就是爲什麼最後一點在REPL中起作用的原因。在編譯後的代碼中,只有編譯代碼,並且只有運行時環境,除非您在代碼中使用eval,這可以使您重新編譯時間....等待我的頭疼,但我希望這可以讓事情變得清晰: )