cf. 7 キーワード,マップそしてリスト – Keywords, maps and dicts – Elixir
Elixir には、キーワードリストとマップという2つの連想データ構造がある。
キーワードリスト
2要素のタプルのリストのうち、タプルの1つ目の要素(つまりキー)がアトムであるような構造を Elixir ではキーワードリストと呼んでいる。
iex(1)> list = [{:a, 1,}, {:b, 2}] [a: 1, b: 2]
返り値にあるように、キーワードリストには [a: 1, b: 2]
のような記法がある。
iex(2)> list == [a: 1, b: 2] true
要素にアクセスするには []
にキーを指定する。
iex(3)> list[:a] 1
キーワードリストはあくまでもリストなので、例えば ++
で連結できる。
iex(4)> [a: 0] ++ list [a: 0, a: 1, b: 2]
上のように、キーが重複しても構わない。もっともこれの何がうれしいのか。
キーが複数あるとき、値を得ようとすると前にある値を返すことに注意。
iex(5)> list = [a: 0, a: 1, b: 2] [a: 0, a: 1, b: 2] iex(6)> list[:a] 0
キーワードリストの重要な特徴は2つ:
- キーの順序を開発者が指定したとおりに保持する
- キーを複数回与えることができる
この特徴のため、キーワードリストは関数へオプションを渡すためのデフォルトのメカニズムになっている。例えば、if/2
はこんな書き方もできる。
iex(7)> if(false, [do: :this, else: :that]) :that
一般に、キーワードリストは引数の最後にあるので、角カッコはつけなくてもいい。
マップ
マップは Ruby でいうところの Hash だ。
iex(8)> map = %{:a => 1, 2 => :b} %{2 => :b, :a => 1} iex(9)> map[:a] 1 iex(10)> map[2] :b
マップの特徴は:
- キーをどんな値にもできる
- キーは順番通りにならない
マップのキーがすべてアトムの場合には、キーワードリストと似たような構文(キーワード構文)を使うことができる。
iex(11)> map =%{a: 1, b: 2} %{a: 1, b: 2}
パターンマッチングもできる。
iex(12)> %{} = %{a: 1, b: 2} %{a: 1, b: 2} iex(13)> %{a: a} = %{a: 1, b: 2} %{a: 1, b: 2} iex(14)> a 1 iex(15)> %{c: c} = %{a: 1, b: 2} ** (MatchError) no match of right hand side value: %{a: 1, b: 2}
マップのパターンマッチングは、部分的にマッチする。だからからのマップはすべてのマップにマッチする一方、存在しないキーにマッチさせようとするとエラーになる。
アトムのキーにはアクセスするための特別の構文が用意されている。
iex(15)> map = %{:a => 1, 2 => :b} %{2 => :b, :a => 1} iex(16)> map.a 1
更新のための構文もある。
iex(17)> %{map | :a => 2} %{2 => :b, :a => 2} iex(18)> %{map | :c => 3} ** (KeyError) key :c not found in: %{2 => :b, :a => 1} (stdlib) :maps.update(:c, 3, %{2 => :b, :a => 1}) (stdlib) erl_eval.erl:255: anonymous fn/2 in :erl_eval.expr/5 (stdlib) lists.erl:1263: :lists.foldl/3
更新するキーは存在するキーでなければいけない。存在しないキーに対して更新しようとするとエラーになる。
ディクショナリ
ディクショナリはインターフェイスのようなもの(Elixir ではビヘイビアと呼ぶ)で、キーワードリストもマップもこのインターフェイスを実装している。
iex(18)> keyword = [] [] iex(19)> map = %{} %{} iex(20)> Dict.put(keyword, :a, 1) [a: 1] iex(21)> Dict.put(map, :a, 1) %{a: 1}
このように、Dict モジュールを使うとキーワードリストとマップを同じように扱える。