1. ホーム
  2. clojure

ClojureでMapのキーと値を繰り返し処理する方法とは?

2023-11-11 17:44:31

質問

次のようなマップがあり、反復処理を行いたい。

(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) は2項目からなるベクターのリストを返します。 (実際にはそれらは clojure.lang.MapEntry ですが、2項目ベクトルのように振る舞います)。

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

doseq は他のものと同じようにそのseqを反復することができます。 コレクションを扱う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"]

を使うことができます。 keyval または firstsecond または 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"

もっと簡潔に言うと、マップの各エントリーの半分を、デストラクチャリングを使って 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"