Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/http/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何在Clojure中迭代映射键和值?_Clojure - Fatal编程技术网

如何在Clojure中迭代映射键和值?

如何在Clojure中迭代映射键和值?,clojure,Clojure,我有以下要迭代的映射: (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

我有以下要迭代的映射:

(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…])
将针对
x
中的每个项目迭代
y

相反,您应该在映射本身上迭代一次<代码>(seq some map)将返回两个项向量的列表,每个项向量对应于映射中的每个键/值对。(实际上它们是
clojure.lang.MapEntry
,但其行为类似于两项向量。)

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"
更简单地说,您可以使用destructuring将地图条目的每一半绑定到可以在
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"

只是对Brian的答案的简短补充:

您的原始版本也可以写如下

(doseq [[k v] (map vector (keys db) (vals db))]
  (println (str k " " v)))
在这种情况下,这显然是愚蠢的。但一般来说,这也适用于不相关的输入序列,它们不是来自同一映射。

您可以简单地执行

(map(fn[[kv]](prnk)(prnv)){:a1:b2}

结果是:

:a
1
:b
2

这就是你想要的吗?

不完全清楚你是否在试图解决打印值(副作用)以外的问题,如果这就是你想要的,那么我认为上面的
doseq
解决方案将是最惯用的。如果您想对映射的键和值执行一些操作,并返回相同类型的数据结构,那么您应该查看
reduce kv
,您可以找到相应的文档


reduce
类似,
reduce kv
接受一个函数、一个起始值/累加器和一些数据,但在这种情况下,数据是一个映射而不是一个序列。该函数通过三个参数:累加器、当前键和当前值。如果您确实想进行一些数据转换并返回一些数据,这对我来说似乎是适合这项工作的工具。

感谢您的解释和示例;他们非常有帮助。据我所知,作为后续行动,
doseq
适用于存在“副作用”的情况。如果我不想要任何“副作用”,比如打印,我该怎么办?clojure.core/for是doseq的纯版本:
(对于[[kv]{1234}](+kv))=>(37)
与其说“想要”副作用,不如说“想要”副作用“需要”副作用。
doseq
for
之间的区别是:……1)
for
增加了懒散性:
for
不会在迭代中生成下一个元素,直到“调用代码”明确需要该元素“。另一方面,
doseq
将迭代元素直到耗尽:如果有副作用需要执行,则应尽快执行(即在不间断循环中)
:a
1
:b
2