この記事の目的
個人的にコレクションとマップがわからなくなったので、備忘を兼ねて。
Elixirにおけるimmutable特性について
Elixirではデータがimmutableなので、変数の値を変更できない。
値を変更しようとした際は、変数のコピーを作成する。
変数のコピーを作成した場合、データ型によってメモリ効率の善し悪しがある。
リスト
リストはheadとtailによる連結リストのデータ構造となっている。
連結リストなので、要素の順番が存在する。
要素の追加・削除を行う操作に適している。
iex> [ head | tail ] = [1 , 2, 3]
[1, 2, 3]
iex> head
1
リストの操作
リストの操作は
- 要素の参照
- 要素の個数を参照
- リストの要素を操作
が出来る。リストの要素を操作する場合、リストの先頭または末尾に値を追加(削除、変更)を行う。
いずれの場合も、コピー元のデータが格納されているメモリを再利用して要素の操作を行うため、メモリ上の無駄は少ない。
要素を参照
リストの要素を参照する場合は、Enumモジュールのat/2関数を使う。要素のインデックスは0から始まる。
iex> list = [1, 2, 3]
[1, 2, 3]
iex)> Enum.at(list, 0)
1
iex> Enum.at(list, 2)
3
要素の個数を参照
リストにおける要素の個数を参照する場合は、length/1関数を使う。リストの要素を末尾までカウントするため、処理は低速となる。
iex> list = [1, 2, 3]
[1, 2, 3]
iex> length(list)
3
先頭に要素を追加
リストは連結リストなので、先頭から順にデータを追っていく。
そのため、先頭に値を追加する場合は高速で実行できる。
iex> list = [1, 2, 3]
[1, 2, 3]
iex> list = [0 | list]
[0, 1, 2, 3]
末尾に要素を追加
リストは、末尾に値を追加する場合は低速になる。
iex> list = [1, 2, 3]
[1, 2, 3]
iex> list ++ [4]
[1, 2, 3, 4]
タプル
タプルはインデックスを利用してメモリ上にデータを隣接して保持するデータ型。
中括弧{}を使って表現する
iex> tuple1 = {"hoge", "foo", "bar", 1}
iex> tuple2 = {1, :a, "boo"}
タプルの操作
タプルはメモリ上にデータが隣接されたデータ型のため、
- 要素の個数が固定されている
- 要素の追加・削除ができない
- 要素を変更すると全ての要素がコピーされため、メモリ上の無駄が発生する
- 要素を取得、または要素の個数を取得する操作が高速
- 要素の順番が存在する
- 要素を取得する際はインデックスを用いる
という性質がある。
要素を取得
タプルから要素を取得する場合はインデックスを用いて、elem/1関数を使う。高速で実行できる。
iex> tuple = {"hoge", "foo", "bar", 1}
{"hoge", "foo", "bar", 1}
iex> elem(tuple, 1)
"foo"
要素の個数を取得
タプルから要素の個数を取得する場合はtuple_size/1関数を使う。高速で実行できる。
iex> tuple = {"hoge", "foo", "bar", 1}
{"hoge", "foo", "bar", 1}
iex> tuple_size(tuple)
4
要素を変更
以下の"hoge"、"bar"の要素は元の変数でメモリに保持していたものではなく、コピーされた結果新しくメモリに格納されたデータとなる。
iex> tuple = {"hoge", "foo", "bar", 1}
{"hoge", "foo", "bar", 1}
iex> tuple = {"hoge", 2, "bar", 3}
{"hoge", 2, "bar", 3}
キーワードリスト
キーワードリストは
- {key, value}の形のタプルを要素にしたリスト
- keyのデータ型はアトムでなくてはならない
- 同一keyが複数存在することが可能
- リストなので要素の順序が存在する
iex> keyword_list = [{:a, 1}, {:b, 2}] # キーワードリスト
[a: 1, b: 2]
iex> keyword_list = [a: 1, b: 2] # 要素を中括弧{}無しの key: value で記述可能
[a: 1, b: 2]
iex)> keyword_list = [{:a, 1}, {:b, 2, :c, 3}] # キーワードリストではない
[{:a, 1}, {:b, 2, :c, 3}]
キーワードリストの操作
キーワードリストはkey:valueの特性と、リストの特性を持っているため、
- keyを用いてvalueを参照可能
- 同一keyが複数存在する場合は、より先頭に近い要素のvalueを参照
- リストの先頭・末尾に要素として{key, value}の形のタプルを追加・削除可能
となる。
要素(value)を参照
要素(value)にはkeyを用いて参照できる。参照する場合はアトムで指定する。
iex> keyword_list = [{:a, 1}, {:b, 2}]
[a: 1, b: 2]
iex> keyword_list[:a]
1
要素の個数を参照
リストと同様、要素の個数を参照する場合は、length/1関数を使う。
iex> keyword_list = [{:a, 1}, {:b, 2}]
[a: 1, b: 2]
iex> length(keyword_list)
2
先頭に要素を追加
リスト同様、++/2関数を使って要素を追加する。
iex> keyword_list = [{:a, 1}, {:b, 2}]
[a: 1, b: 2]
iex> keyword_list = [a: 0] ++ [{:a, 1}, {:b, 2}] # 表記が異なっていても追加可能
[a: 0, a: 1, b: 2]
末尾に要素を追加
リスト同様、末尾に値を追加する場合は低速になる。
iex> keyword_list = [{:a, 1}, {:b, 2}]
[a: 1, b: 2]
iex> keyword_list = [{:a, 1}, {:b, 2}] ++ [c: 3] # 表記が異なっていても追加可能
[a: 1, b: 2, c: 3]
マップ
マップは、「キー:値」のセットのデータ型。
キーを使って値を参照するが、順序が存在しない。
iex> map = %{ name: "Dave", likes: "Programming", where: "Dallas" }
%{likes: "Programming", name: "Dave", where: "Dallas"}
マップの操作と更新
マップの操作にはMap APIを用いる。
マップのキーを全て参照
iex> map = %{ name: "Dave", likes: "Programming", where: "Dallas" }
%{likes: "Programming", name: "Dave", where: "Dallas"}
iex> Map.keys map
[:likes, :name, :where]
マップの値を全て参照
iex> Map.values map
["Programming", "Dave", "Dallas"]
マップのキーから値を参照
マップが入れ子になっている場合はget_in関数が有効
iex> map[:name]
"Dave"
iex> map.name
"Dave"
iex> get_in(map, [:name])
"Dave"
iex> Map.get(map, :name)
"Dave"
マップに「キー:値」を追加
iex> Map.put map, :also_likes, "Ruby"
%{also_likes: "Ruby", likes: "Programming", name: "Dave", where: "Dallas"}
マップの更新
iex> map = %{ map | name: "Jane" }
%{likes: "Programming", name: "Jane", where: "Dallas"}
マップから「キー:値」を削除
iex> Map.drop map, [:where, :likes]
%{name: "Dave"}
マップから「キー:値」を取り出して新しいマップを作成する
iex> { value, updated_map} = Map.pop map, :where
{"Dallas", %{likes: "Programming", name: "Dave"}}
iex> updated_map
%{likes: "Programming", name: "Dave"}