带循环和重复的Clojure NullPointerException

时间:2017-04-06 21:30:49

标签: 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}

在尝试解释堆栈跟踪后,我猜NullPointerException与以下行有关: (conj result-set (fn head))。 stacktrace以及结果集为空的事实让我相信inc操作以某种方式被调用nil作为输入。

我很高兴解释为什么会发生这种错误

上面提到的(shortend)stacktrace看起来像这样:

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 

1 个答案:

答案 0 :(得分: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会在f的每个输入上调用函数v,然后将该调用的结果放入在hash-set创建的if中。首先。

问题在于if语句的控制流逻辑。在fn之后,执行流程仍在继续。因此,即使fhead nil,函数if(我重命名为when,而不是将其保留为宏的名称)也被调用你的意图。

正如最初编码的那样,if(更明显是In [23]: x = ['data_0', 'data_1', 'data_100', 'data_101', 'data_109', 'data_11', 'data_110'] In [24]: sorted(x) Out[24]: ['data_0', 'data_1', 'data_100', 'data_101', 'data_109', 'data_11', 'data_110'] )没有做任何有用的事情。但是一旦我意识到你想要一个key,但是过早关闭了这个问题,那么答案就会落到实处。因此,微小的变化解决了这个问题 - 你的基础逻辑是合理的 - 修正后的功能才起作用。