我有下面的映射,我想迭代:
(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)))
我想出了一个解决方案,但Brian的(见下文)更合乎逻辑。
(let [k (keys db) v (vals db)]
(do (println (apply str (interpose " " (interleave k v))))))
这是预期的行为。(doseq [x ... y ...])
将遍历y
中的每一项,以获取x
中的每一项。
(seq some-map)
将返回一个包含两项向量的列表,一个用于映射中的每个键/值对。(实际上它们是clojure.lang.MapEntry
,但表现得像2项向量。)
user> (seq {:foo 1 :bar 2})
([:foo 1] [:bar 2])
doseq
可以像迭代其他序列一样迭代该序列。像Clojure中处理集合的大多数函数一样,在迭代集合之前,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"]
您可以使用key
和val
,或first
和second
,或nth
,或get
从这些向量中获取键和值。
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"
更简单地说,您可以使用解构将map条目的每一半绑定到可以在doseq
表单中使用的一些名称。这是习惯用法:
user> (doseq [[k v] db] (prn k v))
:subprotocol "mysql"
:username "usr"
:classname "com.mysql.jdbc.Driver"
:subname "//100.100.100.100:3306/clo"
:password "pwd"
你可以直接做
(map (fn [[k v]] (prn k) (prn v)) {:a 1 :b 2})
结果是:
:a
1
:b
2
这是你要找的吗?
对Brian的回答做一个简短的补充:
您的原始版本也可以写成如下:
(doseq [[k v] (map vector (keys db) (vals db))]
(println (str k " " v)))
在这种情况下,这显然是愚蠢的。但一般来说,这也适用于不相关的输入序列,它们不是来自同一个映射。
如果您试图解决的问题不仅仅是打印值(副作用),则不完全清楚,如果这就是您所要做的,那么我认为上面的doseq
解决方案将是最惯用的。如果您想对映射的键和值执行一些操作,并返回相同类型的数据结构,那么您应该查看reduce-kv
,您可以在这里找到相关文档
和reduce
一样,reduce-kv
也接受一个函数、一个起始值/累加器和一些数据,但是这里的数据是一个映射而不是一个序列。该函数传递三个参数:累加器、当前键和当前值。如果您确实想要进行一些数据转换并返回一些数据,那么对我来说,这似乎是适合这项工作的工具。