如何在 Clojure 中正确 "filter" 映射?
How to "filter" maps properly in Clojure?
我已经使用 Clojure 玩了一段时间,但我陷入了一些我认为对很多人来说非常微不足道的事情......但不是我。我有以下代码;
;; Define a Record structure
(defrecord Person [first-name last-name age occupation])
(def john (->Person "John" "Frusciante" 50 "Guitarist"))
;; People map
(def people {"1" john
"2" (->Person "Pablo" "Neruda" 90 "Poet")
"3" (->Person "Stefan" "Zweig" 120 "Author")
}
)
(defn get-120-year-old-guy
[peeps]
(filter #(= (:age %) 120) peeps)
)
(println "who's 120?: " (get-120-year-old-guy people))
这调用 returns 一个空列表。我知道我检索值的方式有问题,但看不到到底是什么。
您可以通过临时更改函数来了解发生了什么:
(defn get-120-year-old-guy
[peeps]
(filter (fn [m] (println (type m) m)) peeps))
打印:
(clojure.lang.MapEntry [1 #user.Person{:first-name John, :last-name Frusciante, :age 50, :occupation Guitarist}]
clojure.lang.MapEntry [2 #user.Person{:first-name Pablo, :last-name Neruda, :age 90, :occupation Poet}]
clojure.lang.MapEntry [3 #user.Person{:first-name Stefan, :last-name Zweig, :age 120, :occupation Author}]
)
注意每个条目是 MapEntry
。在您的尝试中,您将 :age
应用于整个 MapEntry
(returns nil
),而不仅仅是人。
我认为使用完全匿名函数进行解构是最简单的方法:
(defn get-120-year-old-guy
[peeps]
(filter (fn [[_ person]] (= (:age person) 120)) peeps))
输出:
who's 120?: ([3 #user.Person{:first-name Stefan, :last-name Zweig, :age 120, :occupation Author}])
@leetwinski 指出了一个更惯用的解决方案,它完全取消了显式函数:
(filter (comp #{120} :age val) people)
细分:
(defn get-120-year-old-guy [peeps]
(filter (comp ; And (comp)ose all three checks together
#{120} ; Then test if it's in the set of #{120}
:age ; Then get the age
val) ; Get the value from the MapEntry
peeps))
如果您查看外部映射中的第一项,您会发现每一项都是从字符串到人物的 clojure.lang.MapEntry
。键为“1”,值为 Person 记录:
> (first people)
["1"
{:first-name "John",
:last-name "Frusciante",
:age 50,
:occupation "Guitarist"}]
要过滤 :age 字段,您必须首先获取 {key, value} 对的值。一种方法是在从 Person 映射获取 :age 之前使用 val 函数获取它。那么你的过滤函数是:
(defn get-120-year-old-guy
[peeps]
(filter #(= (:age (val %)) 120) peeps)
)
> (println "who's 120?: " (get-120-year-old-guy people))
who's 120?: ([3 #challenges.anag.Person{:first-name Stefan, :last-name Zweig, :age 120, :occupation Author}])
另一种选择是 destructure clojure.lang.MapEntry
:
(defn get-120-year-old-guy
[peeps]
(filter (fn [[_ v]] (= (:age v) 120)) peeps)
)
在这里您可以看到 (fn [[_ v]] ...
,其中 _
用作未使用的 key
.
的占位符
我已经使用 Clojure 玩了一段时间,但我陷入了一些我认为对很多人来说非常微不足道的事情......但不是我。我有以下代码;
;; Define a Record structure
(defrecord Person [first-name last-name age occupation])
(def john (->Person "John" "Frusciante" 50 "Guitarist"))
;; People map
(def people {"1" john
"2" (->Person "Pablo" "Neruda" 90 "Poet")
"3" (->Person "Stefan" "Zweig" 120 "Author")
}
)
(defn get-120-year-old-guy
[peeps]
(filter #(= (:age %) 120) peeps)
)
(println "who's 120?: " (get-120-year-old-guy people))
这调用 returns 一个空列表。我知道我检索值的方式有问题,但看不到到底是什么。
您可以通过临时更改函数来了解发生了什么:
(defn get-120-year-old-guy
[peeps]
(filter (fn [m] (println (type m) m)) peeps))
打印:
(clojure.lang.MapEntry [1 #user.Person{:first-name John, :last-name Frusciante, :age 50, :occupation Guitarist}]
clojure.lang.MapEntry [2 #user.Person{:first-name Pablo, :last-name Neruda, :age 90, :occupation Poet}]
clojure.lang.MapEntry [3 #user.Person{:first-name Stefan, :last-name Zweig, :age 120, :occupation Author}]
)
注意每个条目是 MapEntry
。在您的尝试中,您将 :age
应用于整个 MapEntry
(returns nil
),而不仅仅是人。
我认为使用完全匿名函数进行解构是最简单的方法:
(defn get-120-year-old-guy
[peeps]
(filter (fn [[_ person]] (= (:age person) 120)) peeps))
输出:
who's 120?: ([3 #user.Person{:first-name Stefan, :last-name Zweig, :age 120, :occupation Author}])
@leetwinski 指出了一个更惯用的解决方案,它完全取消了显式函数:
(filter (comp #{120} :age val) people)
细分:
(defn get-120-year-old-guy [peeps]
(filter (comp ; And (comp)ose all three checks together
#{120} ; Then test if it's in the set of #{120}
:age ; Then get the age
val) ; Get the value from the MapEntry
peeps))
如果您查看外部映射中的第一项,您会发现每一项都是从字符串到人物的 clojure.lang.MapEntry
。键为“1”,值为 Person 记录:
> (first people)
["1"
{:first-name "John",
:last-name "Frusciante",
:age 50,
:occupation "Guitarist"}]
要过滤 :age 字段,您必须首先获取 {key, value} 对的值。一种方法是在从 Person 映射获取 :age 之前使用 val 函数获取它。那么你的过滤函数是:
(defn get-120-year-old-guy
[peeps]
(filter #(= (:age (val %)) 120) peeps)
)
> (println "who's 120?: " (get-120-year-old-guy people))
who's 120?: ([3 #challenges.anag.Person{:first-name Stefan, :last-name Zweig, :age 120, :occupation Author}])
另一种选择是 destructure clojure.lang.MapEntry
:
(defn get-120-year-old-guy
[peeps]
(filter (fn [[_ v]] (= (:age v) 120)) peeps)
)
在这里您可以看到 (fn [[_ v]] ...
,其中 _
用作未使用的 key
.