简体   繁体   English

为什么我在下面的代码中获得NPE?

[英]Why do I get NPE in the following code?

The following code executes as expected but gives a NullPointerException at the end. 以下代码按预期执行,但最后给出NullPointerException What am I doing wrong here? 我在这做错了什么?

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

(For properly syntax highlighted code, go here .) (有关正确语法突出显示的代码,请转到此处 。)

Take a look at the expansion that is happening: 看一下正在发生的扩展:

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

As you can see, there is an extra pair of parentheses around your expansion, which means that Clojure tries to execute the result of the println function, which is nil. 如您所见,扩展周围有一对额外的括号,这意味着Clojure尝试执行println函数的结果,即nil。

To fix this I'd suggest modifying the macro to include a "do" at the front, eg 为了解决这个问题,我建议修改宏以在前面加上“do”,例如

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

Since the OP asked for other possible ways of writing this macro (see comments on the accepted answer), here goes: 由于OP要求其他可能的方法来编写这个宏(请参阅对已接受答案的评论),这里有:

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

This expands to something like 这扩展到类似的东西

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

Note that the fn forms whose values will be bound to c are collected in a vector at macro-expansion time. 注意,其值将被绑定到cfn形式在宏扩展时被收集在向量中。

Needless to say, the original version is simpler, thus I think (do ...) is the perfect fix. 毋庸置疑,原始版本更简单,因此我认为(do ...)是完美的解决方案。 :-) :-)

Example interaction: 示例交互:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM