2010-07-14 41 views
7

Mã sau đây thực hiện như mong đợi nhưng cung cấp cho một NullPointerException ở cuối. Tôi làm gì sai ở đây?Tại sao tôi nhận được NPE trong mã sau?

(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=> 

(Ví đúng cú pháp nổi bật mã, đi here.)

Trả lời

11

Hãy nhìn vào việc mở rộng đó đang xảy ra:

(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]))) 

Như bạn thấy, có một cặp thêm dấu ngoặc đơn xung quanh sự mở rộng của bạn, có nghĩa là Clojure cố gắng thực hiện kết quả của hàm println, đó là số không.

Để khắc phục điều này, tôi khuyên bạn nên sửa đổi macro để bao gồm "làm" ở phía trước, ví dụ:

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

+1 jejej, được sử dụng cho những ngoặc :) – OscarRyz

+0

+1, bất kỳ cách khác để khắc phục? – missingfaktor

+2

Chắc chắn, bạn có thể viết lại nó để mở rộng đến một 'liều lượng' vv Nhưng tại sao? Đây là một giải pháp hoàn toàn hợp lý và thay đổi đối với mã hiện tại của bạn là tối thiểu; Tôi muốn nói với nó. –

6

Kể từ khi OP hỏi cách khác có thể viết macro này (xem ý kiến ​​về câu trả lời được chấp nhận), ở đây đi:

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

này mở rộng đến một cái gì đó giống như

(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)) 

Lưu ý rằng các biểu mẫu fn có giá trị sẽ được ràng buộc với c được thu thập trong một vectơ tại thời gian mở rộng macro.

Không cần phải nói, phiên bản gốc đơn giản hơn, vì vậy tôi nghĩ rằng (do ...) là bản sửa lỗi hoàn hảo. :-)

Ví dụ tương tác:

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, cảm ơn câu trả lời. :-) – missingfaktor

Các vấn đề liên quan