2009-10-07 15 views
5

Clojure中下面的表達式的偉大工程:空指針Clojure中在身體上運行多個表情doseq時

(doseq [x '(1 2 3 4)] (println x)) 

這一次給了我一個空指針:

(doseq [x '(1 2 3 4)] ((println x)(println "x"))) 

它產生下面的輸出:

user=> (doseq [x '(1 2 3 4)] ((println x)(println "x"))) 
1 
x 
java.lang.NullPointerException (NO_SOURCE_FILE:0) 
user=> (.printStackTrace *e) 
java.lang.NullPointerException (NO_SOURCE_FILE:0) 
    at clojure.lang.Compiler.eval(Compiler.java:4639) 
    at clojure.core$eval__5182.invoke(core.clj:1966) 
    at clojure.main$repl__7283$read_eval_print__7295.invoke(main.clj:180) 
    at clojure.main$repl__7283.doInvoke(main.clj:197) 
    at clojure.lang.RestFn.invoke(RestFn.java:426) 
    at clojure.main$repl_opt__7329.invoke(main.clj:251) 
    at clojure.main$legacy_repl__7354.invoke(main.clj:292) 
    at clojure.lang.Var.invoke(Var.java:359) 
    at clojure.main.legacy_repl(main.java:27) 
    at clojure.lang.Repl.main(Repl.java:20) 
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) 
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) 
    at java.lang.reflect.Method.invoke(Method.java:597) 
    at jline.ConsoleRunner.main(ConsoleRunner.java:69) 
Caused by: java.lang.NullPointerException 
    at user$eval__266.invoke(NO_SOURCE_FILE:26) 
    at clojure.lang.Compiler.eval(Compiler.java:4623) 
    ... 14 more 
nil 

只需在doseq gi的身體周圍添加一組額外的括號我指的是空指針。 我在做什麼錯?

+1

我已經想出瞭如何解決這個問題,但我並沒有真正理解它。 當doseq執行它的主體時,它評估那裏的表達式。 ((println x)(println x)) 以上計算結果爲println在head(nil)處返回值的列表。然後,在下一次迭代時,它會嘗試評估該列表。 解決方案是使用'do'。 (dosel [x'(1 2 3 4)](do(println x)(println「x」))) –

回答

10

好,你已經想出瞭解決方案,所以只是一些提示來解釋行爲:

在Clojure中(就像Lisp,Scheme等一樣),一切都是表達式,而表達式是原子或列表。關於名單,Clojure的手冊說

非空列表被認爲是調用 要麼特殊形式,宏或 功能。呼叫的格式爲 (操作員操作數*)。

在你的榜樣,身體((println x) (println x))是列表,操作者本身它的Clojure必須評估,以獲得實際操作的表達式。也就是說,你說「評估第一個表達式並將其返回值作爲調用第二個表達式的函數」。然而,如您注意到的,println僅返回nil。如果nil被解釋爲運算符,則這導致NullPointerException

您的代碼適用於(do (println x) (println x)),因爲do是一種特殊形式,依次評估每個表達式並返回最後一個表達式的值。這裏的do是操作符,而println是表達式的操作數。

要理解此行爲的用處,請注意,函數是Clojure中的第一類對象,例如,您可以將函數作爲其他函數的結果返回。例如,採取以下代碼:

(doseq [x '(1 2 3 4)] ((if (x > 2) 
    (fn [x] (println (+ x 2))) 
    (fn [x] (println (* x 3)))) x)) 

在這裏,我動態搞清楚運營商在序列中的元素來調用。首先,評估if表達式。如果x大於2,則if評估打印x + 2的功能,否則評估爲打印x * 3的功能。該功能應用於序列的x

5

我看你已經意識到這個問題,但是請注意,你不需要做:

(doseq [x '(1 2 3 4)] (println x) (println "x")) 

doseq是(顧名思義)做一個已經:)