在宏

2011-10-04 40 views
5

做一個命名空間限定的函數名我有一大堆的映射,並從由外部系統定義了一些代碼的功能:在宏

(defn translate-from-ib-size-tick-field-code [val] 
    (condp = val 
    0 :bid-size 
    3 :ask-size 
    5 :last-size 
    8 :volume)) 

(defn translate-to-ib-size-tick-field-code [val] 
    (condp = val 
    :bid-size 0 
    :ask-size 3 
    :last-size 5 
    :volume 8)) 

我想打一個宏來刪除重複:

#_ (translation-table size-tick-field-code 
         {:bid-size 0 
         :ask-size 3 
         :last-size 5 
         :volume 8})  

我開始喜歡這個宏:

(defmacro translation-table [name & vals] 
    `(defn ~(symbol (str "translate-to-ib-" name)) [val#] 
    (get [email protected] val#))) 

生成的函數體似乎是正確的,但函數名是錯誤的:

re-actor.conversions> (macroexpand `(translation-table monkey {:a 1 :b 2})) 
(def translate-to-ib-re-actor.conversions/monkey 
    (.withMeta (clojure.core/fn translate-to-ib-re-actor.conversions/monkey  
    ([val__10589__auto__] 
     (clojure.core/get {:a 1, :b 2} val__10589__auto__))) (.meta ... 

我想「翻譯到IB-」顯示爲函數名稱的一部分,而不是一個前綴命名空間,因爲它橫空出世。

我該如何使用clojure宏來做到這一點?如果我只是做錯了,不應該爲此使用宏,請讓我知道,但我也想知道如何創建這樣的函數名,以提高我對clojure和宏的一般理解。謝謝!

回答

7

的宏觀問題是雙重的:

1)您使用的引用傳遞給macroexpand形式,其命名空間內資格的符號時反引號:

`(translation-table monkey {:a 1 :b 2}) 
=> (foo.bar/translation-table foo.bar/monkey {:a 1, :b 2}) 

其中foo.bar是什麼名稱空間

2)您正在使用符號name構造defn項目的名稱,該名稱在名稱空間限定時會將其字符串化爲「foo.bar/monkey」。這裏有一個版本,將工作:

(defmacro translation-table [tname & vals] 
    `(defn ~(symbol (str "translate-to-ib-" (name tname))) [val#] 
    (get [email protected] val#))) 

請注意,我們得到的tname名字沒有命名空間的一部分,使用name功能。

至於宏是否是這裏的正確的解決方案,可能不是:-)對於一個簡單的情況就是這樣,我可能只是用地圖:

(def translate-from-ib-size-tick-field-code 
    {0 :bid-size 
    3 :ask-size 
    5 :last-size 
    8 :volume}) 

;; swap keys & vals 
(def translate-to-ib-size-tick-field-code 
    (zipmap (vals translate-from-ib-size-tick-field-code) 
      (keys translate-from-ib-size-tick-field-code))) 

(translate-from-ib-size-tick-field-code 0) 
=> :bid-size 

(translate-to-ib-size-tick-field-code :bid-size) 
=> 0 

如果速度是最重要的,檢查出case

+0

謝謝,這正是我所尋找的兩點。 –

3

一些不請自來的建議:(get [email protected] val#)是非常可疑的。你的宏指稱要採取任何數量的論據,但是如果它超過兩個,它只會做一些毫無意義的事情。例如,

(translation-table metric {:feet :meters} 
          {:miles :kilometers} 
          {:pounds :kilograms}) 
從一個可怕的轉換表

之外,擴展到代碼,總是拋出異常:

(defn translate-to-ib-metric [val] 
    (get {:feet :meters} 
     {:miles :kilometers} 
     {:pounds :kilograms} 
     val))) 

get並不需要很多的參數,當然,這不是真的什麼反正你的意思是。最簡單的解決將是隻允許兩個參數:

(defmacro translation-table [name vals] 
    (... (get ~vals val#))) 

但要注意的,這意味着該值映射被重建每次函數調用時 - 問題,如果它的價格昂貴來計算,或有副作用。所以如果我把它寫成一個宏(雖然看到賈斯汀的答案 - 你爲什麼?),我會這樣做:

(defmacro translation-table [name vals] 
    `(let [vals# ~vals] 
    (defn ~(symbol (str "translate-to-ib-" name)) [val#] 
     (get vals# val#)))) 
+0

謝謝!我並沒有打算把「&」放在那裏,直到我看到你的評論,最終導致了我的問題。 –