個人的にまとめてみました。
全体像
・ リスト [1, true, 2]
・ タプル {false, 1, "abc"}
・ キーワードリスト(上記の組み合わせ) [{:a, 1}, {:b, 2}]
= [a: 1, b: 2]
・ マップ %{:a => 1, "abc" => :dog}
, %{a: 1, b: 2}
・ 構造体(フィールドの存在を保証したマップ)
すべてにおいて、どんな型も入れられる。
リスト [ ]
[1, 2, true, "abc"]
要素の一つ一つが次の要素のアドレスを指してる
- 特定の要素アクセスやサイズ取得は遅い(順番にアクセスする必要があるため)
-
先頭に追加するのは速い
[0] ++ list
タプル { }
{1, 2, true, "abc"}
連続したメモリに保存
- 特定の要素アクセスやサイズ取得が速い
- 要素の追加や変更は遅い(その度に新しいタプルをつくる)
変更
put_elem(たぷる, インデックス, 内容)
キーワードリスト [hoge: foo, ...]
"最初の要素がアトムになっているタプル"のリスト。
list = [{:a, 1}, {:b, 2}]
list = [a: 1, b: 2] #こうもかける
list[:a] #アクセス
特徴
- キーの順序を開発者が指定したとおりに保持する
- キーを複数回与えることができる
※同じキーがある場合は前のが取得される
list = [a: 0, a: 1, b: 2]
list[:a] #0
パターンマッチング
iex> [a: a] = [a: 1]
[a: 1]
iex> a
1
iex> [a: a] = [a: 1, b: 2]
** (MatchError) no match of right hand side value: [a: 1, b: 2]
iex> [b: b, a: a] = [a: 1, b: 2]
** (MatchError) no match of right hand side value: [a: 1, b: 2]
※キーワードリストを操作するKeywordモジュールがあります。
マップ %{:hoge => foo, ...}
map = %{:a => 1, 2 => :b}
- マップはキーをどんな値にもできる
- 1つのキーが1つの値をもつ
- マップのキーは順番通りにならない
同じキーを渡すと最後が勝つ
%{1 => 1, 1 => 2}
%{1 => 2}
キーが全部atomならもっとシンプルに書ける
map = %{a: 1, b: 2}
map.a
%{map | :a => 2} #変更(追加じゃない)
マップはパターンマッチングに使いやすい
キーワードリストとは対照的に使いやすい。
左辺にキーが全て一致してないくても大丈夫。
iex> %{} = %{:a => 1, 2 => :b}
%{:a => 1, 2 => :b}
iex> %{:a => a} = %{:a => 1, 2 => :b}
%{:a => 1, 2 => :b}
iex> a
1
iex> %{:c => c} = %{:a => 1, 2 => :b}
** (MatchError) no match of right hand side value: %{2 => :b, :a => 1}
※マップを操作するMapモジュールがあります。
キーワードリストとマップの使い分け
キーワードリストは単にリストなので、長いリストはキーを見つけるのがより長くなり、要素を数えるのも長くなる。
だからキーワードリストは主にオプションに使われる。
もし沢山の要素を保持しなければならなかったり、1つのキーが多くとも1つの値しか持たないことを保証したい場合はマップを使った方がいい。
キーワードリストもマップも「Dicts」
ディクショナリ。
ディクショナリはインターフェイスのようなもの。
キーワードリストもマップも、ディクショナリのインターフェイスを実装している。
※ Elixirではインターフェイスをbehaviours(ビヘイビア)と呼ぶ。
iex> keyword = []
[]
iex> map = %{}
%{}
iex> Dict.put(keyword, :a, 1)
[a: 1]
iex> Dict.put(map, :a, 1)
%{a: 1}
リストもマップもEnnumerble
リストとマップはEnnumerbleプロトコルを実装しているのでEnumモジュールやStreamモジュールで操作することができる。
Enumの例
iex> Enum.reduce([1, 2, 3], 0, fn(x, acc) -> x + acc end)
6
iex> Enum.map([1, 2, 3], fn(x) -> x * 2 end)
[2, 4, 6]
Enumのパイプライン
1..100_000 |> Enum.map(&(&1 * 3)) |> Enum.filter(odd?) |> Enum.sum
構造体
構造体は、マップのフィールドの存在をコンパイル時に保証する。
※ 構造体はの実体はただのマップ。
構造体の定義
defmodule User do
defstruct name: "john", age: 27
end
> john = %User{} #インスタンス生成
%User{age: 27, name: "john"}
> john.name #アクセス
"john"
> meg = %{john | name: "meg"} #値の変更
%User{age: 27, name: "meg"}
> %{meg | oops: :field} #マップと同じく存在しないキーには変更できない
** (ArgumentError) argument error
パターンマッチング
iex> %User{name: name} = john
%User{age: 27, name: "john"}
iex> name
"john"
iex> %User{} = %{}
** (MatchError) no match of right hand side value: %{}
※構造体はディクショナリではない
ネストしたデータ構造を操作するput_inとupdate_inが便利