[英]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. 注意,其值将被绑定到c
的fn
形式在宏扩展时被收集在向量中。
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.