爲了解決您的問題,我不得不做一些重構,我將包括每個版本的代碼以顯示獲得理想結果所需的步驟。
;; 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))
非常感謝你的詳細解答。我對Clojure仍然很陌生,我從答案中學到了很多東西。 –