2012-12-01 121 views
2

對於clojure及其宏,我自己寫了一個宏,它可以從諸如「abc」之類的字符列表中生成長度爲'n'的所有字符串。因此,對於n = 2,輸出應該是「aa」「ab」「ac」「ba」「bb」「bc」「ca」「cb」「cc」。我以下面的函數作爲模板開始: (defn mkstr [n v](for [i v j v](str i j)))。在「綁定」中重複'v'並創建多少變量應該是'n'的函數;在這個特定的情況下:2.clojure宏eval

在閱讀了關於'報價''引用'等等,然後遵循一個關於宏,很多反覆試驗和一些簡單的運氣的優秀在線教程,我設法產生以下功能和宏,無論'n'的值如何,它都會給出所需的輸出。真正困難的部分是生成'for'綁定所需的可變數量的代碼。

(defn mkvars [n] 
"Gives a list of 'n' unique symbols" 
(let [vc (repeatedly n #(gensym))] vc)) 

(defmacro mkcoms [n syms] 
"Generates a list of possible combinations of length 'n' from a string of symbols" 
`(let [vs# (mkvars ~n) sy# ~syms 
    forarg# (vec (interleave vs# (repeat ~n sy#)))] 
    `(for ~forarg# (str [email protected]#)))) 

現在我的「真實」的問題或不理解的是,獲得輸出,我必須這樣做: (EVAL(mkcoms len個字符))。爲什麼這隻能使用'eval'?誠然,它可以像現在這樣使用,但有些事情感覺錯誤。

+0

評論被我刪除。 – Brian

回答

2

您的宏正在返回一個帶引號的表單,這就是爲什麼當您將它傳遞給eval時它會起作用。我不明白宏的作用是什麼,所以我希望這個解釋是你所追求的。

宏應該生成它代表的代碼並將其返回。你的宏產生一個引用的表單。如果您刪除了反引號的外層,這看起來像意圖成爲執行擴展(代碼)的代碼的一部分,而不是生成的代碼,您可以在宏擴展時執行該代碼:

(defmacro mkcoms [n syms]                          
    "Generates a list of possible combinations of length 'n' from a string of symbols"           
    (let [vs  (mkvars n)                          
     sy  syms                            
     forarg (vec (interleave vs (repeat n sy)))]                    
    `(for ~forarg (str [email protected])))) 

這聽起來像你之後,雖然我承認我不明白爲什麼你希望這發生在'編譯時'和運行時。

+0

謝謝你的回答。它消除了一些關於宏寫入的誤解,但也展示瞭如何將常規函數代碼與生成的代碼混合使用,反之亦然。經過進一步的實驗後,我意識到如何耗時的自動gensym似乎是。 – Brian