2014-10-11 26 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

相關問題