2013-07-30 17 views
1

我寫了兩個這樣的函數,但正如你看到他們大部分是相同的,所以我想編寫一個宏來簡化它們。如何使用defmacro簡化一些代碼

我明白我教科書中的簡單宏例子,但我不知道如何編寫自己的例子。

這裏是我的代碼:

(defn load-dict 
     ; "Load database from a txt file previous saved" 
     [fname] 
     (with-open [rdr (io/reader fname)] 
        (doseq [line (line-seq rdr)] 
         (println line) 
         (def vvv (clojure.string/split line #"\s")) 
         ;(println (str "count of vvv is " (count vvv))) 
         (if (< 1 (count vvv)) 
          (add- dict (gen-word (nth vvv 0) (nth vvv 2) (nth vvv 1)))) 
         ))) 
(defn load-article 
     ; "Load article from a txt file" 
     [fname] 
     (with-open [rdr (io/reader fname)] 
        (doseq [line (line-seq rdr)] 
         (println line) 
         (def vvv (clojure.string/split line #"\s")) 
         ;(println (str "count of vvv is " (count vvv))) 
         (if (< 1 (count vvv)) 
          (add- article vvv)) 
         ))) 

我應該寫這樣一個宏:

我其實不知道怎麼寫這樣一個宏。我只是討厭重複的代碼。

PS,拖曳功能正常工作。我不在乎這是代碼的一部分。

+0

使用使用函數來擺脫重複的代碼 – Ankur

回答

5

我會使用let塊而不是def。使用def將綁定一個var並在你的命名空間中定義vvv。宏並不是真的需要。你可以簡化你的代碼是這樣的:

(defn load-from 
    "Load database from a txt file previous saved" 
    [fname load-fn] 
    (with-open [rdr (io/reader fname)] 
       (doseq [line (line-seq rdr)] 
        (println line) 
        (let [vvv (clojure.string/split line #"\s")] 
         (when (< 1 (count vvv)) 
         (load-fn vvv)))))) 

並調用它像這樣

(load-from "myfile.txt" #(add- dict (apply gen-word (take 3 %)))) 
(load-from "myfile.txt" #(add- article %)) 
+0

您的代碼同樣簡潔,除了(帶3%)子句只取第一個3的成員,序列不在這裏。所以我修改了函數gen-word來解決這個問題。 – user2545464

+0

對不起,我沒有看到你的(第nth vvv 0),(nth vvv 2)...的順序不是順序的。如果gen-word現在很樂意接受前三個vvv,我會保持原樣。或者如果你有任何願望,我可以改變它。 –

3

user1944838是正確的在這裏,你不需要一個宏,因爲宏使代碼,這不需要它們,在某些情況下稍微難以使用(例如,您不能將它傳遞給mapapply),但在實踐中使用函數更可取。瞭解如何正確編寫宏是非常重要的。

我會把它寫成一個模板宏,它將一個名字傳遞給每個單詞,然後調用傳遞給宏的主體,宏將通過該符號名稱來使用該單詞。

(defmacro with-loaded-article                                    
    [[name-for-line fname] & body]                                   
    `(with-open [rdr# (io/reader ~fname)]                                 
    (doseq [line# (line-seq rdr#)]                                  
     (println line#)                                      
     (let [~name-for-line (clojure.string/split line# #"\s")]                           
     [email protected])))) 
  • [name-for-line fname]表達destructures第一個參數到將被用於產生一個符號,並且它將解析到值的單個「結合的形式」。這種格式在「with- *」宏中很常見,例如with-open,除了這裏我只採用一種綁定形式來保持代碼更小。
  • rdr#line#語法中的符號引用是一種稱爲「auto gensyms」的語法引用的一個特性,它使得以#結尾的語法引號中的任何符號被替換爲結果表達式中唯一的一致符號。
  • [email protected]是引起body在這種情況下插入而不添加()的句法引號的拼接 - 不引號功能。

我們可以看到這與macroexpand-1pprint提示擴展:(use 'clojure.pprint)

hello.core> (pprint (macroexpand-1 
         `(with-loaded-article [line "tmp.txt"] (println line))))                     
(clojure.core/with-open                                     
[rdr__6337__auto__ (clojure.java.io/reader "tmp.txt")]                             
(clojure.core/doseq                                      
    [line__6338__auto__ (clojure.core/line-seq rdr__6337__auto__)]                           
    (clojure.core/println line__6338__auto__)                                
    (clojure.core/let                                      
    [hello.core/line (clojure.string/split line__6338__auto__ #"\s")]                          
    (clojure.core/println hello.core/line))))                                

而當我們運行所產生的代碼中,我們得到了文件的行爲序列:

hello.core> (with-loaded-article [line "tmp.txt"] (println line))                           
hello world                                        
[hello world]                                        
world hello                                        
[world hello]                                        
and internet hello as well                                     
[and internet hello as well]