2016-02-14 98 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仍然很陌生,我从答案中学到了很多东西。 –