2016-02-14 94 views
0

我寫下面的函數將s2合併到s1中,並且任何其「id」值與s1中任何元素相同的元素都不會合並。函數的意外行爲

(defn into-unique-id 
    [s1, [x2 & xs2]] 
    (if x2 (if (empty (filter (fn [x] (= (get x "id") (get x2 "id"))) s1)) (into-unique-id (conj s1 x2) xs2) (into-unique-id s1 xs2)) 
     s1)) 

我在REPL中嘗試了以下內容。

gridizela.core=> (def s1 [{"id" "1"} {"id" "2"}]) 
#'gridizela.core/s1 
gridizela.core=> (into-unique-id s1 [{"id" "1"}]) 
[{"id" "1"} {"id" "2"} {"id" "1"}] 

正如您所看到的,結果並非如我所料,最後一個元素不應添加。

任何幫助,將不勝感激。

回答

6

爲了解決您的問題,我不得不做一些重構,我將包括每個版本的代碼以顯示獲得理想結果所需的步驟。

;; original 
(defn into-unique-id 
    [s1, [x2 & xs2]] 
    (if x2 (if (empty (filter (fn [x] (= (get x "id") (get x2 "id"))) s1)) (into-unique-id (conj s1 x2) xs2) (into-unique-id s1 xs2)) 
     s1)) 

原始代碼是不同尋常的佈局,在某種程度上,它使我很難閱讀,我很難理解。所以我的第一步是應用正常的縮進。

;; indentation 
(defn into-unique-id-2 
    [s1, [x2 & xs2]] 
    (if x2 
    (if (empty (filter (fn [x] (= (get x "id") 
            (get x2 "id"))) 
         s1)) 
     (into-unique-id (conj s1 x2) xs2) 
     (into-unique-id s1 xs2)) 
    s1)) 

我到現在還沒有完全得到代碼,但是我看到了一些小的變化,可以使它更容易閱讀。 cond幾乎總是你想要的東西,而不是嵌套的條件。我使用,,作爲空格來區分cond中的條件子句的結果子句。

;; simplification 
(defn into-unique-id-3 
    [s1, [x2 & xs2]] 
    (cond (not x2) 
     ,, s1 
     (empty (filter (fn [x] (= (get x "id") 
            (get x2 "id"))) 
         s1)) 
     ,, (into-unique-id (conj s1 x2) xs2) 
     :else 
     (into-unique-id s1 xs2))) 

在這一點上,我終於看到了錯誤:(empty x)將返回truthy任何輸入不是零,即使是空的集合。我們在這裏實際需要的是具有欺騙性的同名(但非常不同)的功能。

;; fix -- empty is always truthy here 
(defn into-unique-id-4 
    [s1, [x2 & xs2]] 
    (cond (not x2) 
     ,, s1 
     (empty? (filter (fn [x] (= (get x "id") 
            (get x2 "id"))) 
         s1)) 
     ,, (into-unique-id (conj s1 x2) xs2) 
     :else 
     (into-unique-id s1 xs2))) 

下一個我看到的不是filter/empty?我們可以使用內置的some

;; simplification -- we have idiomatic functions for this 
(defn into-unique-id-5 
    [s1, [x2 & xs2]] 
    (cond (not x2) 
     ,, s1 
     (some (fn [x] (= (get x "id") 
         (get x2 "id"))) 
         s1) 
     ,, (into-unique-id s1 xs2) 
     :else 
     ,, (into-unique-id (conj s1 x2) xs2))) 

早期我注意到,這是有效地用手做了reduce,所以作爲最後一步,我要展示功能爲減少。

;; structural simplification - we have idiomatic higher order functions 
;; that we can use instead of recursion 
(defn into-unique-id-6 
    [s1, coll] 
    (reduce 
    (fn [acc el] 
    (if (some (fn [x] 
       (= (get x "id") 
        (get el "id"))) 
       acc) 
     acc 
     (conj acc el))) 
    s1 
    coll)) 
+1

非常感謝你的詳細解答。我對Clojure仍然很陌生,我從答案中學到了很多東西。 –