2010-07-14 76 views
7

以下代碼按預期方式執行,但在最後給出NullPointerException。我在這裏做錯了什麼?爲什麼我會在下面的代碼中獲得NPE?

(ns my-first-macro) 

(defmacro exec-all [& commands] 
    (map (fn [c] `(println "Code: " '~c "\t=>\tResult: " ~c)) commands)) 

(exec-all 
    (cons 2 [4 5 6]) 
    ({:k 3 :m 8} :k) 
    (conj [4 5 \d] \e \f)) 

; Output: 
; Clojure 1.2.0-master-SNAPSHOT 
; Code: (cons 2 [4 5 6]) => Result: (2 4 5 6) 
; Code: ({:k 3, :m 8} :k) => Result: 3 
; Code: (conj [4 5 d] e f)  => Result: [4 5 d e f] 
; java.lang.NullPointerException (MyFirstMacro.clj:0) 
; 1:1 user=> #<Namespace my-first-macro> 
; 1:2 my-first-macro=> 

(對於正確語法高亮代碼,請here

回答

11

看看擴張正在發生:

(macroexpand '(exec-all (cons 2 [4 5 6]))) 
=> 
((clojure.core/println "Code: " (quote (cons 2 [4 5 6])) "\t=>\tResult: " (cons 2 [4 5 6]))) 

正如你可以看到,有一個額外的對這意味着Clojure試圖執行println函數的結果,即零。

爲了解決這個問題,我建議修改宏以在前面包括「do」,例如,

(defmacro exec-all [& commands] 
    (cons 'do (map (fn [c] `(println "Code: " '~c "\t=>\tResult: " ~c)) commands))) 
+0

+1 jejej,獲得使用這些括號:) – OscarRyz 2010-07-14 15:12:02

+0

+1,任何其他方式來解決? – missingfaktor 2010-07-14 15:15:11

+2

當然,你可以重寫它以擴大到'doseq'等,但爲什麼?這是一個完全合理的解決方案,對現有代碼的更改很少;我會說堅持下去。 – 2010-07-14 16:19:20

6

由於OP要求寫這個宏(見接受的答案評論),這裏的其他可能的方式去:

(defmacro exec-all [& commands] 
    `(doseq [c# ~(vec (map (fn [c] 
          `(fn [] (println "Code: " '~c "=> Result: " ~c))) 
         commands))] 
    (c#))) 

這擴展到像

(doseq [c [(fn [] 
      (println "Code: "  '(conj [2 3 4] 5) 
         "=> Result: " (conj [2 3 4] 5))) 
      (fn [] 
      (println "Code: "  '(+ 1 2) 
         "=> Result: " (+ 1 2)))]] 
    (c)) 

請注意,fn表單的值將綁定到c的表單會在宏擴展時收集到向量中。

不用說,原始版本更簡單,因此我認爲(do ...)是完美的解決方案。 :-)

示例交互:

user=> (exec-all (conj [2 3 4] 5) (+ 1 2))                          
Code: (conj [2 3 4] 5) => Result: [2 3 4 5] 
Code: (+ 1 2) => Result: 3 
nil 
+0

+1,謝謝你的回答。 :-) – missingfaktor 2010-07-14 18:13:26

相關問題