2011-12-27 66 views
0

我在第一个Clojure程序中苦于使用NPE,并希望得到一些帮助。Clojure中的NullPointerException减少方法

call-elem是包含结构: -

  • :id,像some.packagename.SomeClass.someMethod
  • :calls(孩子)的字符串,

定义如下其它call-elem结构的向量:

(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需要class-mapcall-elem作为参数。 class-mapclass-namemethod-map的地图。 method-mapmethod-namevariation的列表的地图。每个variation是具有密钥:class-name,:method-name:calls的地图。 :calls是其他变体的列表。所述foo函数应返回一个矢量具有两个元素: -

  • 第一元素是一个类的地图与对应于call-elem新项,并且其孩子
  • 第二元件是对应于参数的变化图call-elem

下面是代码:

(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))) 

当我尝试运行FO llowing代码:

(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))) 

以下是堆栈跟踪:

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) 

回答

2

让我们仔细看看foo中的reduce。我怀疑你试图传递一个两元素集合作为初始值reduce的累加器。但是,使用不加引号的括号会导致(class-map '())被视为要评估的表达式。更糟糕的是,它是有效的,因为class-mapclojure.lang.PersistentArrayMap,其实施IFn

结果,而不是创建你调用(class-map '())两个元素的列表,其中最有可能返回nil,因为有一个在class-map没有'()关键。之后,在foo'中,您尝试将nil绑定到[class-map variations-list]。繁荣。空指针异常。

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} 
+0

现货!谢谢!你对改进代码有什么建议吗?正如我所说,这是我第一次尝试Clojure,所以我们欢迎任何建议。 :) – 2011-12-27 20:09:18