2017-04-06 38 views
3

我是新來的Clojure和我目前堅持下面的代碼會拋出一個NullPointerException當我運行它像這樣:Clojure的NullPointerException異常帶環和復發

(mapset inc [1 1 2 2])

(defn mapset 
    [fn v] 
    (loop [[head & tail] v result-set (hash-set)] 
     (if (nil? head) 
     result-set) 
     (conj result-set (fn head)) 
     (recur tail result-set))) 

當我打印出結果 - 設置在if塊它打印一個空集,而我期望一個像這樣的集:#{2 3}

試圖解釋stacktrace後,我猜NullPointerException與下面一行有關: (conj result-set (fn head))。 stacktrace和結果集爲空的事實導致我相信inc操作以某種方式被調用爲nil作爲輸入。

我很高興一下爲什麼這個錯誤發生

任何解釋提到的(shortend)堆棧跟蹤看起來是這樣的:

java.lang.NullPointerException 

Numbers.java: 1013 clojure.lang.Numbers/ops 
Numbers.java: 112 clojure.lang.Numbers/inc 
core.clj: 908 clojure.core/inc 
core.clj: 903 clojure.core/inc 
REPL: 6 user/mapset 
REPL: 1 user/mapset 

回答

3

我做了一些小的變化:

(defn mapset [f v] 
    (loop [[head & tail] v 
     result-set (hash-set)] 
    (if (nil? head) 
     result-set 
     (let [res (f head)] 
     (recur tail (conj result-set res)))))) 

(defn x-1 [] 
    (mapset inc [1 1 2 2])) 

(x-1) 
;;=> #{3 2} 

所以現在mapset將在來自v的每個輸入上調用功能f,然後將該呼叫的結果放入0123最初創建的。

問題出在您的if語句的控制流邏輯中。執行流程在if之後繼續。因此,即使當headnil時,函數fn(我將其重命名爲f,而不是將其保留爲宏的名稱)也被調用,但這不是您想要的。

由於最初編碼if(這將更清楚地是一個when)沒有什麼用處。但是,一旦我意識到你的意思是有一個if,但過早地關閉了這個孤零零的人,那麼回答就開始了。因此,微小的變化解決了這個問題 - 你的基礎邏輯是合理的 - 而修正後的功能剛剛起作用。

+0

非常感謝。我怎麼能不明白...你也提醒我使用讓更好的清晰度 – Epiglottis

+0

np。我使用'let'來幫助調試 - 在一行上減少了開銷。你當然可以擺脫它,我可能會 - 因爲'res'只使用一次,是一個愚蠢的名字。 –

+0

你當然是對的 – Epiglottis

相關問題