もやもやエンジニア

IT系のネタで思ったことや技術系のネタを備忘録的に綴っていきます。フロント率高め。

Elixir 入門 その6 - Keyword lists, Maps

Elixir では連想配列を表現する手段として keyword list と mapの2つがある。

Keyword list

  • 特定の条件のtupleのリストはkeyword list として扱われる
  • listの拡張みたいな存在なので、listの特徴をそのまま受け継いでいる。ゆえにlistが長くなればなるほどサイズを取得したり、後ろのアイテムを取得したりする場合は、処理が遅くなる。
iex> list = [{:a, 1}, {:b, 2}]
[a: 1, b: 2]

# アクセスするときはキー名を指定できる。
iex> list[:a]
1

# 直接書いても通る
iex> list = [a: 1, b: 2]

# 一つ目の要素がatomでない場合はtupleのlistとして扱われる
iex> list = [{"a", 1}, {"b", 2}]
[{"a", 1}, {"b", 2}]

# 要素数が異なる場合はtupleのlistとして扱われる
iex>list = [{:a, 1}, {:b, 2, 4}]
[{:a, 1}, {:b, 2, 4}]

# キーの重複は許容される
iex> list = [{:a, 1}, {:a, 2}]
[a: 1, a: 2]

# アクセスするとListの先頭から先に見つかった要素が表示される。
iex> list[:a]
1
  • 通常のListと同じ操作が使える
iex> list = [{:a, 1}, {:b, 2}]
[a: 1, b: 2]

iex> list ++ [c: 3]
[a: 1, b: 2, c: 3]

iex> [a: 0] ++ list
[a: 0, a: 1, b: 2]

Maps

  • key -value のデータを扱いたいときはMapを使う。
  • %{ } で囲む
  • 関数の引数でMapを使ったパターンマッチを使うと便利
  • 割と最近導入された仕組みで小さいMapを扱うのに適している。容量の大きなKeyが必要になるデータはHashDict を使ったほうが性能が良い。
iex> map = %{:a => 1, :b => 2}
%{a: 1, b: 2}

# キー名を指定してアクセスできる
iex> map[:a]
1

# 存在しないキーはnilが返る
iex> map[:c]
nil

# キーのデータ型が統一されていなくても通る
iex> map = %{:a => 1, 2 => :b}
%{2 => :b, :a => 1}

# キーの重複は許可されず、後勝ち
iex> map = %{:a => 1, :a => 2}
%{a: 2}

# valiableをキーに指定することはできない
iex>  key = :a
:a

iex> map = %{key => 1}
** (CompileError) iex:61: illegal use of variable key in map key
    (elixir) src/elixir_translator.erl:344: :elixir_translator.translate_arg/3
    (elixir) src/elixir_map.erl:154: anonymous fn/7 in :elixir_map.translate_map/4
    (stdlib) lists.erl:1352: :lists.mapfoldl/3
    (elixir) src/elixir_map.erl:153: :elixir_map.translate_map/4
    (elixir) src/elixir_translator.erl:17: :elixir_translator.translate/2

# mapにvaliableでアクセスすることはできる
iex> map[key]
1

# mapを更新する
iex> %{map | :a => 2}
%{2 => :b, :a => 2}

# 更新するときに存在しないキーを指定すると例外
iex> %{map | :c => 3}
** (ArgumentError) argument error
    (stdlib) :maps.update(:c, 3, %{a: 1, b: 2})
    (stdlib) erl_eval.erl:255: anonymous fn/2 in :erl_eval.expr/5
    (stdlib) lists.erl:1261: :lists.foldl/3
  • パターンマッチ
iex> map = %{:a => 1, :b => 2, :c => 3}

# 必ずマッチする
iex> %{} = map
%{a: 1, b: 2, c: 3}

# 部分的にマッチする
iex> %{:b => 2} = map
%{a: 1, b: 2, c: 3}

# 値を束縛する
iex> %{:a => a, :b => 2, :c => c} = map
%{a: 1, b: 2, c: 3}
iex> a
1
iex> c
3

# マッチしないと例外
iex> %{:a => 2} = map
** (MatchError) no match of right hand side value: %{a: 1, b: 2, c: 3}

Dicts

  • Keyword list と Map は dictionaryが実装されている(behaviourと呼ばれてるらしい)ので Dict moduleのメソッドが使える