如何遍历映射键和值?

我有我想要迭代的以下映射:

(def db {:classname "com.mysql.jdbc.Driver" :subprotocol "mysql" :subname "//100.100.100.100:3306/clo" :username "usr" :password "pwd"}) 

我尝试了以下,而不是打印键和值一次 ,它反复打印键和值作为各种组合:

 (doseq [k (keys db) v (vals db)] (println (str k " " v))) 

我提出了一个解决scheme,但布赖恩(见下文)更合乎逻辑。

 (let [k (keys db) v (vals db)] (do (println (apply str (interpose " " (interleave kv)))))) 

这是预期的行为。 (doseq [x ... y ...])将遍历y中每个项目中的每个项目。

相反,你应该迭代一次地图本身。 (seq some-map)将返回两个项目向量的列表,其中一个用于映射中的每个键/值对。 (真的,他们是clojure.lang.MapEntry ,但行为像2项vector。)

 user> (seq {:foo 1 :bar 2}) ([:foo 1] [:bar 2]) 

doseq可以像其他任何一样迭代该seq。 与Clojure中与集合doseq工作的大多数函数一样, doseq在迭代之前会在集合中内部调用seq 。 所以你可以简单地这样做:

 user> (doseq [keyval db] (prn keyval)) [:subprotocol "mysql"] [:username "usr"] [:classname "com.mysql.jdbc.Driver"] [:subname "//100.100.100.100:3306/clo"] [:password "pwd"] 

您可以使用keyval ,或者firstsecondnth ,或者从这些向量中获取键和值。

 user> (doseq [keyval db] (prn (key keyval) (val keyval))) :subprotocol "mysql" :username "usr" :classname "com.mysql.jdbc.Driver" :subname "//100.100.100.100:3306/clo" :password "pwd" 

更简洁地说,您可以使用解构来将每个映射条目的一半绑定到您可以在doseq表单中使用的某些名称。 这是惯用的:

 user> (doseq [[kv] db] (prn kv)) :subprotocol "mysql" :username "usr" :classname "com.mysql.jdbc.Driver" :subname "//100.100.100.100:3306/clo" :password "pwd" 

你可以简单的做

(map (fn [[kv]] (prn k) (prn v)) {:a 1 :b 2})

结果是:

 :a 1 :b 2 

这是你在找什么?

Brian的答案只是一个简短的补充:

你的原始版本也可以写成如下。

 (doseq [[kv] (map vector (keys db) (vals db))] (println (str k " " v))) 

在这种情况下,这显然是愚蠢的。 但总的来说,这也适用于不相关的input序列,这些input序列不是来自同一个映射。