---
title: Clojrueのmapの使い方（データ構造の方）
tags: []
categories: ["Programming", "Lisp", "Clojure"]
date: 2010-03-22T16:50:57Z
updated: 2010-03-22T18:33:40Z
---

<p>Shibuya.lispでClojureはデータ構造が貧弱といわれていましたが、Mapはもうちょっと評価されてもいいと思うので使い方メモ。</p>
<p>Clojure1.1.0で確認。</p>
<h3>Map作成</h3>
<p>リテラルは{}です。key valueの組み合わせを並べます。必ず要素数は偶数。
またClojureでは半角カンマは空白とみなされるので、key valueの後に半角カンマを入れると見やすい。</p>
<pre class="prettyprint lang-cl">
user> (def m {:a 1 :b "a" :c 'foo})
#'user/m
user> m
{:a 1, :b "a", :c foo}
user> (class m)
clojure.lang.PersistentArrayMap
</pre>
<p>リテラルのはArrayMapですね(仕様ではない気がするが)。array-map関数でも作成できます。</p>
<pre class="prettyprint lang-cl">
user> (def am (array-map :a 1 :b "a" :c 'foo))
#'user/am
user> am
{:a 1, :b "a", :c foo}
user> (class am)
clojure.lang.PersistentArrayMap
</pre>
<p>HashMapはhash-map関数でつくれます。入力と順番が違う点に注意。</p>
<pre class="prettyprint lang-cl">
user> (def hm (hash-map :a 1 :b "a" :c 'foo))
#'user/hm
user> hm
{:a 1, :c foo, :b "a"}
user> (class hm)
clojure.lang.PersistentHashMap
</pre>
<p>sorted-mapもあるよ。</p>
<pre class="prettyprint lang-cl">
user> (def sm (sorted-map :a 1 :b "a" :c 'foo))
#'user/sm
user> sm
{:a 1, :b "a", :c foo}
user> (class sm)
clojure.lang.PersistentTreeMap
</pre>
<h3>普通のMap</h3>
<h4>要素取得</h4>
<p><code>(key map)</code>または<code>(map key)</code>の形式で値取得。</p>
<pre class="prettyprint lang-cl">
user> (:a m)
1
user> (:b m)
"a"
user> (m :a)
1
</pre>
<p>keyを先頭にできるのはkeyが<code>Symbol</code>か<code>Keyword</code>のときだけだと思う。多分。<br/>
この場合、keyを関数のように適用できる利点がある。変な例だけど</p>
<pre class="prettyprint lang-cl">
user> (map :b [m m m])
("a" "a" "a")
</pre>
<p>関数では<code>get</code>が使える。</p>
<pre class="prettyprint lang-cl">
user> (get m :a)
1
</pre>
<p>keyやvalueのシーケンスがほしいときは<code>keys</code>、<code>vals</code>で。</p>
<pre class="prettyprint lang-cl">
user> (keys m)
(:a :b :c)
user> (vals m)
(1 "a" foo)
</pre>
<h4>要素追加(更新)</h4>
<p><code>assoc</code>です。</p>
<pre class="prettyprint lang-cl">
user> (def updated-m (assoc m :d 100))
#'user/updated-m
user> updated-m
{:d 100, :a 1, :b "a", :c foo}
user> (assoc m :d 100 :e 200)
{:e 200, :d 100, :a 1, :b "a", :c foo}
user> (assoc m :b 100)
{:a 1, :b 100, :c foo}
</pre>
<p>immutableなのはいわずもがな。</p>
<h4>要素削除</h4>
<p><code>dissoc</code>です。</p>
<pre class="prettyprint lang-cl">
user> (dissoc updated-m :c)
{:d 100, :a 1, :b "a"}
user> (dissoc updated-m :d :c)
{:a 1, :b "a"}
</pre>
<p>immutableな(ry</p>
<h3>ネストしたMap</h3>
<p>便利なデータ構造として使うからにはネストしたい。たとえばこんな感じ。</p>
<pre class="prettyprint lang-cl">
(def nested-map {:lisp {:cl {:c 'sbcl, :java 'abcl},
                        :scheme {:c 'gauche, :java 'kawa}}, 
                 :ruby {:c 'matz-ruby :java 'jruby}})
</pre>
<p>いままでの知識だけでsbclをaclに変えたいとき、</p>
<pre class="prettyprint lang-cl">
user> (let [lisp-map (:lisp nested-map),
            cl-map (:cl lisp-map)]
       (assoc lisp-map :lisp (assoc cl-map :c 'acl)))
{:lisp {:c acl, :java abcl}, :cl {:c sbcl, :java abcl}, :scheme {:c gauche, :java kawa}}
</pre>
<p>こんな感じになります。さらにネストが深くなると。。。目も当てられないことになりますね。<br />
こんなときのためにちゃんと関数が用意されています。
</p>
<h4>要素追加(更新)</h4>
<p><code>assoc-in</code>です。使い方は<code>(assoc-in m [k & ks] v)</code>。↑の例だと</p>
<pre class="prettyprint lang-cl">
user=> (assoc-in nested-map [:lisp :cl :c] 'acl)
{:lisp {:cl {:c acl, :java abcl}, :scheme {:c gauche, :java kawa}}, :ruby {:c matz-ruby, :java jruby}}
</pre>
<p>追加ももちろんできる。</p>
<pre class="prettyprint lang-cl">
user> (assoc-in nested-map [:lisp :cl :lisp] 'oreore-lisp)
{:lisp {:cl {:lisp oreore-lisp, :c sbcl, :java abcl}, :scheme {:c gauche, :java kawa}}, :ruby {:c matz-ruby, :java jruby}}
</pre>
<p><s>ちなみに<code>dissoc-in</code>はない。</s>→できた</p>
<h4>要素更新(関数適用版)</h4>
<p>現在の要素に関数を適用させて値を更新する<code>update-in</code>ってのもある。使い方は<code>(update-in m [k & ks] f & args)</code>。<br/>
変な例だけど、こんな感じ。
</p>
<pre class="prettyprint lang-cl">
user> (update-in nested-map [:lisp :cl :c] str "-1.0.36") 
{:lisp {:cl {:c "sbcl-1.0.36", :java abcl}, :scheme {:c gauche, :java kawa}}, :ruby {:c matz-ruby, :java jruby}}
</pre>
<h4>値取得</h4>
<p><code>get-in</code>もあるよー。使い方は<code>(get-in m ks)</code>。</p>
<pre class="prettyprint lang-cl">
user> (get-in nested-map [:lisp :cl :java])
abcl
</pre>
<p>なんで<code>(get-in m k & ks)</code>にしなかったんだろ。。(assoc,updateと合わせるためかな。)</p>

<hr />
<p>
とりあえず、mapの使い方はこんな感じです。(他にも破壊的な<code>assoc!</code>、<code>dissoc!</code>もあるけど、使わないほうが良いと思う。)
<br/>まあまあ使えると思いませんか？
</p>
