Clojure reduce方法中的NullPointerException

Clojure reduce方法中的NullPointerException,clojure,Clojure,在我的第一个Clojure项目中,我正在与NPE作斗争,希望得到一些帮助 调用元素是一个结构,包含:- :id,类似于some.packagename.SomeClass.someMethod的字符串 :调用(子项),其他调用元素的向量结构 定义如下: (defstruct call-struct :id :calls) (defn- class-name [call] (let [id (call :id)] (.substring id 0 (.lastIndexOf id

在我的第一个Clojure项目中,我正在与NPE作斗争,希望得到一些帮助

调用元素
是一个结构,包含:-

  • :id
    ,类似于
    some.packagename.SomeClass.someMethod的字符串
  • :调用
    (子项),其他
    调用元素的向量
    结构
定义如下:

(defstruct call-struct :id :calls)

(defn- class-name [call]
  (let [id (call :id)]
    (.substring id 0 (.lastIndexOf id "."))))

(defn- method-name [call]
  (let [id (call :id)]
    (.substring id (inc (.lastIndexOf id ".")))))
我还有一个方法
foo
,它将
类映射
调用元素
作为参数<代码>类映射是
类名
方法映射
的映射<代码>方法映射是
方法名称
变体列表的映射。每个
变体
都是一个带有键
:类名
:方法名
:调用
的映射<代码>:calls
是其他变体的列表。
foo
函数应返回包含两个元素的向量:-

  • 第一个元素是一个类映射,其中包含与
    调用元素及其子元素相对应的新条目
  • 第二个元素是与参数
    callelem
代码如下:

(declare foo' add-new-variation)

(defn foo [class-map call-elem]
  (let [children (call-elem :calls)
        class-name (class-name call-elem)
        method-name (method-name call-elem)]
    (if (empty? children)
      (let [new-variation {:class-name class-name
                           :method-name method-name
                           :calls []}
            new-class-map (add-new-variation class-map 
                                             class-name method-name 
                                             new-variation)]
        [new-class-map new-variation])
      (let [[new-class-map child-variations-list] 
            (reduce #(foo' %1 %2) (class-map '()) children)
            new-variation {:class-name class-name
                           :method-name method-name
                           :calls child-variations-list}
            new-class-map' (add-new-variation new-class-map 
                                              class-name method-name 
                                              new-variation)]
        [new-class-map' new-variation]))))

(defn foo' [[class-map variations-list] call-elem]
  (let [[new-class-map new-variation] (foo class-map call-elem)]
    [new-class-map (cons new-variation variations-list)]))

(defn add-new-variation [class-map class-name method-name variation]
  (let [method-map (if (contains? class-map class-name)
                     (class-map class-name) {})
        variations-list (if (contains? method-map method-name)
                          (method-map method-name) [])
        new-variations-list (conj variations-list variation)
        new-method-map (assoc method-map method-name new-variations-list)]
    (assoc class-map class-name new-method-map)))
当我尝试运行以下代码时:

(try
  (let [call-elem 
        (struct call-struct "Class1.method1" 
                [(struct call-struct "Class2.method1" [])])]
    (second (foo {} call-elem)))
  (catch Exception ex
         (.printStackTrace ex)))
我得到以下结果:

{:class-name "Class1",
 :method-name "method1",
 :calls ({:class-name "Class2", :method-name "method1", :calls []})}
但是当我尝试运行下面的代码,将调用增加一个级别时,我得到了一个
NullPointerException

(try
  (let [call-elem 
        (struct call-struct "Class1.method1" 
                [(struct call-struct "Class2.method1"
                         [(struct call-struct "Class3.method1" [])])])]
    (second (foo {} call-elem)))
  (catch Exception ex
         (.printStackTrace ex)))
以下是stacktrace:

java.lang.NullPointerException
    at first.simple$foo.invoke(NO_SOURCE_FILE:46)
    at first.simple$foo_SINGLEQUOTE_.invoke(NO_SOURCE_FILE:55)
    at first.simple$foo$fn__1986.invoke(NO_SOURCE_FILE:46)
    at clojure.lang.ArrayChunk.reduce(ArrayChunk.java:58)
    at clojure.core.protocols$fn__5565.invoke(protocols.clj:30)
    at clojure.core.protocols$fn__5543$G__5538__5552.invoke(protocols.clj:11)
    at clojure.core$reduce.invoke(core.clj:5995)
    at first.simple$foo.invoke(NO_SOURCE_FILE:46)
    at first.simple$eval2010.invoke(NO_SOURCE_FILE:6)
    at clojure.lang.Compiler.eval(Compiler.java:6465)
    at clojure.lang.Compiler.eval(Compiler.java:6431)
    at clojure.core$eval.invoke(core.clj:2795)
    at clooj.repl$create_clojure_repl$repl_thread_fn__578$fn__589.invoke(repl.clj:147)
    at clojure.main$repl$read_eval_print__5967.invoke(main.clj:244)
    at clojure.main$repl$fn__5972.invoke(main.clj:265)
    at clojure.main$repl.doInvoke(main.clj:265)
    at clojure.lang.RestFn.invoke(RestFn.java:1523)
    at clooj.repl$create_clojure_repl$repl_thread_fn__578.invoke(repl.clj:145)
    at clojure.lang.AFn.run(AFn.java:24)
    at java.lang.Thread.run(Thread.java:680)

让我们仔细看看
foo
中的
reduce
。我怀疑您试图将两元素集合作为
reduce
累加器的初始值传递。但是,使用不带引号的括号会导致将
(类映射“())
视为要求值的表达式。更糟糕的是,它是有效的,因为
类映射
是一个
clojure.lang.PersistentArrayMap
,它实现了
IFn

因此,您不是在创建一个两元素列表,而是调用
(class map'())
,该列表很可能返回
nil
,因为
类映射中没有
'()
键。之后,在
foo'
中,您试图将
nil
绑定到
[类映射变体列表]
。繁荣空指针异常

reduce
的第二个参数定义为向量应该可以解决这个问题

--- before  2011-12-27 12:52:43.052218334 +0100
+++ after   2011-12-27 12:52:56.785477270 +0100
@@ -13,7 +13,7 @@
                                              new-variation)]
         [new-class-map new-variation])
       (let [[new-class-map child-variations-list]
-            (reduce #(foo' %1 %2) (class-map '()) children)
+            (reduce #(foo' %1 %2) [class-map '()] children)
             new-variation {:class-name class-name
                            :method-name method-name
                            :calls child-variations-list}

让我们仔细看看
foo
中的
reduce
。我怀疑您试图将两元素集合作为
reduce
累加器的初始值传递。但是,使用不带引号的括号会导致将
(类映射“())
视为要求值的表达式。更糟糕的是,它是有效的,因为
类映射
是一个
clojure.lang.PersistentArrayMap
,它实现了
IFn

因此,您不是在创建一个两元素列表,而是调用
(class map'())
,该列表很可能返回
nil
,因为
类映射中没有
'()
键。之后,在
foo'
中,您试图将
nil
绑定到
[类映射变体列表]
。繁荣空指针异常

reduce
的第二个参数定义为向量应该可以解决这个问题

--- before  2011-12-27 12:52:43.052218334 +0100
+++ after   2011-12-27 12:52:56.785477270 +0100
@@ -13,7 +13,7 @@
                                              new-variation)]
         [new-class-map new-variation])
       (let [[new-class-map child-variations-list]
-            (reduce #(foo' %1 %2) (class-map '()) children)
+            (reduce #(foo' %1 %2) [class-map '()] children)
             new-variation {:class-name class-name
                            :method-name method-name
                            :calls child-variations-list}

完全正确谢谢你对改进代码有什么建议吗?正如我所说,这是我第一次尝试Clojure,因此欢迎提出任何建议。:)完全正确谢谢你对改进代码有什么建议吗?正如我所说,这是我第一次尝试Clojure,因此欢迎提出任何建议。:)