【问题标题】:Destructure and process vector of hash-maps in ClojureClojure 中哈希映射的解构和处理向量
【发布时间】:2022-01-09 18:08:33
【问题描述】:

我有一个哈希映射向量,如下所示:

(def my-maps [{:a 1} {:b 2}])

我想遍历每个哈希映射,在循环中给键和值一个更有意义的名称,然后根据其键以不同的方式处理每个哈希映射。

事不宜迟,这是我最好的尝试:

(for [m my-maps]
  (let [my-key-name (key m) my-val-name (val m)]
    (case my-key-name
      :a (println "Found key :a with value " my-val-name)
      :b (println "Found key :b with value " my-val-name))))

然而,这种方法会产生一个相当神秘的错误:

; Error printing return value (ClassCastException) at clojure.core/key (core.clj:1569).
; class clojure.lang.PersistentArrayMap cannot be cast to class java.util.Map$Entry (clojure.lang.PersistentArrayMap is in unnamed module of loader 'app'; java.util.Map$Entry is in module java.base of loader 'bootstrap')

我做错了什么?

【问题讨论】:

  • 每次执行for 的主体时,m 都会绑定到一个地图。 keyval 函数需要地图 entry,而不是地图本身。如果您确定每张地图只有一个元素,请尝试将 m 重新绑定到 (first m)
  • 要使用解构,您可以简单地(let [[my-key my-val] (first m)] ...),因为映射条目是索引集合。

标签: vector clojure key-value destructuring


【解决方案1】:

您可以在for 内部解构(或使用doseq):

(for [[[k v] & _] [{:a 1} {:b 2}]]
  (println "Found key" k "with value" v))

Found key :a with value 1
Found key :b with value 2
=> (nil nil)

【讨论】:

  • 哇,这样更简单更干净!虽然,我必须承认[[[k v] & _] 有点伤脑筋。 & _ 是什么意思?我的最佳猜测是:“忽略地图的所有其他元素”,其中& 用于捕获剩余元素(剩余参数),_ 用于忽略它们;有点像 Erlang 模式匹配。
  • @Leif - Yes- for 遍历向量中的所有元素,[[k v] & _] 解构集合的第一个元素并忽略其余元素。见clojure.org/guides/destructuring
  • [[ k v ]] & _ ] 映射到 { :a 1 nil},即 [[ :a 1]nil] 哈希映射的集合序列。通常 hahs-map 包含由& _ 捕获的其他键值对,例如:[ [ k v ] ] & _ ] 匹配到 {:a 1 :b 2 :c 3}` 其中k 匹配到:a, @ 987654340@ 到 1 其余的 & _[:b 2 :c 3]
【解决方案2】:

为了清楚起见,这里是一个更一般的答案,分为几个步骤:

  (let [my-maps [{:a 1} {:b 2 :c 3}]]
    (doseq [curr-map my-maps]
      (newline)
      (println "curr-map=" curr-map)
      (let [map-entries (seq curr-map)]
        (println "map-entries=" map-entries)
        (doseq [curr-me map-entries]
          (let [[k v] curr-me]
            (println "  curr-me=" curr-me "  k=" k  "  v=" v))))))

有结果

curr-map= {:a 1}
map-entries= ([:a 1])
  curr-me= [:a 1]   k= :a   v= 1

curr-map= {:b 2, :c 3}
map-entries= ([:b 2] [:c 3])
  curr-me= [:b 2]   k= :b   v= 2
  curr-me= [:c 3]   k= :c   v= 3

Clojure 中的MapEntry 对象可以被视为一个二元向量(通过firstsecond 访问)或通过keyval 函数访问的MapEntry。解构形式:

(let [[k v] curr-me] 

MapEntry 对象curr-me 视为一个序列,并将前两个元素提取到kv 中。即使它像矢量一样打印(例如[:a 1]),它确实具有clojure.lang.MapEntry 类型。


原始答案的for 表达式中的解构语法& _ 是“rest args”解构。它会导致将第一个对象之后的所有MapEntry 对象的序列分配给变量_,然后在其余代码中将其忽略。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-29
    • 1970-01-01
    • 1970-01-01
    • 2022-01-08
    • 2017-12-18
    • 1970-01-01
    相关资源
    最近更新 更多