【问题标题】:How to "filter" maps properly in Clojure?如何在 Clojure 中正确“过滤”映射?
【发布时间】:2020-11-05 14:54:39
【问题描述】:

我已经使用 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))

此调用返回一个空列表。我知道我检索值的方式有问题,但看不到究竟是什么。

【问题讨论】:

    标签: dictionary clojure


    【解决方案1】:

    您可以通过临时更改函数来了解正在发生的事情:

    (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(返回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))
    

    【讨论】:

    • 或者只是(filter (comp #{120} :age val) people)
    • @leetwinski 是的,没错。老实说,我已经有两年没有写 Clojure 了。我超级生疏了。
    • 哇。 @Carcigenicate 你切换到了什么?如果这不是秘密)
    • @leetwinski 我在信息安全学校上学,所以为了这门课我改用 Python、C 和汇编。我告诉你,从 Clojure 这样流利、简洁的语言转换到 C 语言是很困难的。
    【解决方案2】:

    如果您查看外部映射中的第一项,您会看到每个项都是从字符串到 Person 的 clojure.lang.MapEntry。键是“1”,值是个人记录:

    > (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}])
    

    【讨论】:

      【解决方案3】:

      另一种选择是destructureclojure.lang.MapEntry

      (defn get-120-year-old-guy
        [peeps]
        (filter (fn [[_ v]] (= (:age v) 120)) peeps)
      )
      

      在这里您可以看到(fn [[_ v]] ...,其中_ 用作未使用的key 的占位符。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-11-04
        • 2012-03-09
        • 2016-06-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-09-25
        • 2012-09-07
        相关资源
        最近更新 更多