2014-10-11 27 views
1

我有以下代码段。它工作的预期。Clojure:在合并和重复操作的上下文中排序映射的不规则行为?

(use '(incanter.stats)) 

(defmacro dbg [body] 
     `(let [x# ~body] 
     (println "dbg:" '~body "=" x#) 
     x#)) 

(defn sorted-map-by-values 
    "create a map sorted in descending order, first by value, then by key" 
    [super-map & reverse] 
    (dbg "Start to sort") 
    (dbg super-map) 
    (let [compare-value (if (nil? reverse) 1 -1)] 
    (into (sorted-map-by 
      (fn [key1 key2] 
      (let [val1 (super-map key1) val2 (super-map key2)] 
       (cond 
       (= val1 val2) (.compareTo (str key2) (str key1)) ; use string representation of list, to overcome that there is no .compareTo for AarrySeq 
       (< (dbg val1) (dbg val2)) compare-value 
       :else (- compare-value))))) 
      super-map)) 
) 

(def search (clojure.string/split "garbage stuff" #"\s")) 

(def candidate (clojure.string/split "stuff" #"\s")) 

(sorted-map-by-values (let [pairs-init (for [x search y candidate] [x y])] 
          (loop [pairs pairs-init distance-map {}] 
           (if (empty? pairs) 
           distance-map 
           (let [pair (sort (first pairs)) 
             updated-map (if (nil? (get distance-map pair)) 
                (merge distance-map {pair (apply incanter.stats/levenshtein-distance pair)}) 
                distance-map)] 
            (recur (rest pairs) updated-map))))) 
          true) 

但是,如果我在下文取代最后形式:

(let [pairs-init (for [x search y candidate] [x y])] 
    (loop [pairs pairs-init distance-map {}] 
    (if (empty? pairs) 
     distance-map 
     (let [pair (sort (first pairs)) 
      updated-map (if (nil? (get distance-map pair)) 
          (sorted-map-by-values ; <- move sorted-map-by-values to here 
          (merge distance-map {pair (apply incanter.stats/levenshtein-distance pair)}) 
          true) 
          distance-map)] 
     (recur (rest pairs) (dbg updated-map)))))) 

然后我得到了一个错误:

java.lang.NullPointerException: null 
      Numbers.java:961 clojure.lang.Numbers.ops 
      Numbers.java:219 clojure.lang.Numbers.lt 
      (Unknown Source) user/sorted-map-by-values[fn] 
      AFunction.java:47 clojure.lang.AFunction.compare 
    PersistentTreeMap.java:311 clojure.lang.PersistentTreeMap.doCompare 
    PersistentTreeMap.java:298 clojure.lang.PersistentTreeMap.entryAt 
    PersistentTreeMap.java:278 clojure.lang.PersistentTreeMap.valAt 
    PersistentTreeMap.java:283 clojure.lang.PersistentTreeMap.valAt 
       RT.java:645 clojure.lang.RT.get 

看来,在线路发生错误:

(< (dbg val1) (dbg val2)) compare-value 

dbg trace为fo llows:

Instarepl: 
dbg: Start to sort = Start to sort 
Instarepl: 
dbg: super-map = {(garbage stuff) 7} 
Instarepl: 
dbg: updated-map = {(garbage stuff) 7} 
Instarepl: 
dbg: val1 = nil 
Instarepl: 
dbg: val2 = 7 

当映射中只有一个映射时,不应该调用比较器函数。通过追踪代码,看起来错误实际发生在loop-recur的第二次迭代中,因为updated-map值的dbg trace显示第一次迭代包括从map-values-sorted-values返回成功,但我无法显示第二个条目sorted-map-by-values,似乎还有未知的另一个条目sorted-map-by-values

我猜那个排序映射可能是一个不同的类型,不能再次应用于排序值?

您能否介绍一些奇怪的行为或者我怀念Clojure语言执行模型的某些部分,这与懒惰评估有关?

非常感谢!

+1

请尝试隔离您的问题案例并将其缩小为几行文本和代码。 – 2014-10-11 10:22:24

+0

我不能让你的代码的头部或脚部,但你的'dbg'输出显示很清楚是什么导致了错误:你的'val1'结尾为'nil',不能用'<'到'7'。 – schaueho 2014-10-11 11:28:15

回答

1

问题是distance-map是一个排序映射,这意味着任何conj都会调用排序函数fn。在你的情况merge是试图做连词的人。

更长的解释:在循环的第二次迭代中,distance-mapsorted-map的实例,然后与{pair (apply incanter.stats/levenshtein-distance pair)}合并。请注意,merge在之前被称为第二次呼叫sorted-map-by-values

这意味着merge正试图添加到sorted-map[(stuff stuff) 0],这意味着正在调用排序映射的排序fn。该类型fn关闭了用于创建它的超级映射的版本,该版本仅包含(garbage stuff)密钥,因此(stuff stuff)的查找为零。

+0

谢谢dAni!所以排序图真的是行为的价值。这是不寻常的,这种行为几乎像副作用! – 2014-10-13 04:28:49

相关问题