71 votes

Comment itérer sur les clés et les valeurs d'une carte en Clojure ?

J'ai la carte suivante que je veux itérer :

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

J'ai essayé ce qui suit, mais au lieu d'imprimer la clé et la valeur une fois il imprime de manière répétée la clé et les valeurs sous la forme de diverses combinaisons :

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

J'ai trouvé une solution, mais celle de Brian (voir ci-dessous) est beaucoup plus logique.

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

102voto

Brian Carper Points 40078

C'est un comportement attendu. (doseq [x ... y ...]) va itérer sur chaque élément dans y pour chaque élément de x .

Au lieu de cela, vous devez itérer sur la carte elle-même une fois. (seq some-map) renvoie une liste de vecteurs à deux éléments, un pour chaque paire clé/valeur de la carte. (En réalité, il s'agit de clojure.lang.MapEntry mais se comportent comme des vecteurs à 2 éléments).

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

doseq peut itérer sur cette séq comme n'importe quelle autre. Comme la plupart des fonctions de Clojure qui travaillent avec des collections, doseq appels internes seq sur votre collection avant de l'itérer. Vous pouvez donc simplement faire ceci :

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

Vous pouvez utiliser key et val ou first et second ou nth ou get pour obtenir les clés et les valeurs de ces vecteurs.

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"

De manière plus concise, vous pouvez utiliser la déstructuration pour lier chaque moitié des entrées de la carte à des noms que vous pouvez utiliser dans la fonction doseq la forme. C'est idiomatique :

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"

1 votes

Merci pour les explications et les exemples ; ils ont été très utiles.

0 votes

En guise de suivi, tel que je le comprends, doseq est destiné aux situations où il y a des "effets secondaires". Que dois-je faire si je ne veux PAS d'effets secondaires comme l'impression ?

15 votes

Clojure.core/for est une version pure de doseq : (for [[k v] {1 2 3 4}] (+ k v)) => (3 7)

29voto

Mrinal Saurabh Points 109

Vous pouvez simplement faire

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

Le résultat est :

:a
1
:b
2

C'est ce que vous recherchiez ?

6voto

kotarak Points 11177

Juste un petit complément à la réponse de Brian :

Votre version originale pourrait également être écrite comme suit.

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

Dans ce cas, c'est évidemment stupide. Mais en général, cela fonctionne également pour des séquences d'entrée non liées, qui ne proviennent pas de la même carte.

5voto

f233d0m Points 165

Il n'est pas tout à fait clair si vous essayez de résoudre quelque chose au-delà de la simple impression des valeurs (effets secondaires), et si c'est tout ce que vous cherchez à faire, je pense que la fonction doseq La solution ci-dessus serait la plus idiomatique. Si vous souhaitez effectuer des opérations sur les clés et les valeurs de la carte et renvoyer le même type de structure de données, vous devriez jeter un coup d'œil à la solution suivante reduce-kv pour lequel vous pouvez trouver les documents pour ici

Comme reduce , reduce-kv accepte une fonction, une valeur de départ/un accumulateur, et des données, mais dans ce cas, les données sont une carte au lieu d'une séquence. La fonction reçoit trois arguments : l'accumulateur, la clé actuelle et la valeur actuelle. Si vous souhaitez effectuer une transformation de données et renvoyer des données, il me semble que c'est l'outil idéal pour cette tâche.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X