Elixir: リストとタプルはいつどちらを使うのか


リストもタプルも、ともに複数のデータの容れ物です。値の型は問わず、いくつでも要素として納められます(「[Elixir入門 02: 型の基本](https://dev.to/gumi/elixir-02--30n1)」参照)。

```elixir
iex> list = [3.14, :pie, true, "Apple"]
[3.14, :pie, true, "Apple"]
iex> tuple = {3.14, :pie, true, "Apple"}
{3.14, :pie, true, "Apple"}
```

けれど、ふたつの仕組みは異なり、したがって使い途も変わります。ふたつの違いとしては、つぎのような点が挙げられます。

| 違い | タプル | リスト |
|:----|:------|:----|
| サイズ | 固定 | 可変 |
| 要素の操作 | 原則としてしない | `List`のほか`Enum`の関数が使える |
| データの参照 | 速い | 遅くなることがある |

# リストの仕組み
リストは「[連結リスト](https://ja.wikipedia.org/wiki/%E9%80%A3%E7%B5%90%E3%83%AA%E3%82%B9%E3%83%88)」です。要素は値とつぎの要素への参照をもちます。要素を特定するインデックスは備えません。

![qiita_12_001_001.png](https://qiita-image-store.s3.amazonaws.com/0/262045/8a3e67ae-1176-b0f1-937d-614c5fb64ac6.png)


# タプルの仕組み
タプルは、連番インデックスをもった静的なデータ構造です。インデックス順に、まとめてメモリに納められます。

![qiita_12_001_002.png](https://qiita-image-store.s3.amazonaws.com/0/262045/0b83549a-f113-6a14-1618-e8721bcd81b8.png)


# サイズに関わる違い
リストは要素を加えたり、除いたりすることが自由です。その数とそれぞれのデータによって、リストのサイズは変わります。タプルは、要素をまとめて納めたときに、サイズが決まります。要素の追加や削除は、実際上行われません。

サイズを得る関数は、リストが[`length/1`](https://hexdocs.pm/elixir/Kernel.html#length/1)、タプルは[`tuple_size/1`](https://hexdocs.pm/elixir/Kernel.html#tuple_size/1)です。つぎのような処理の違いがあります。

| 関数 | 値の求め方 | 処理 |
|:----|:---------|:----|
| length/1 | 数えて返す | 遅い |
| tuple_size/1 | あらかじめ計算されている | 速い |


# 要素の追加や削除などの操作
リストは[`List`](https://hexdocs.pm/elixir/List.html)のほか[`Enum`](https://hexdocs.pm/elixir/Enum.html)モジュールの豊富な関数で操作できます。特定の要素を追加したり、削除したり、変更するだけでなく、すべての要素を取り出して処理できるのです。

タプルを操作する[`Tuple`](https://hexdocs.pm/elixir/Tuple.html)モジュールの[関数](https://hexdocs.pm/elixir/Tuple.html#functions)は多くありません。要素を操作したいときには、リストを使うことが推奨されています。要素を加えたり、書き替えたりする場合は、メモリに新たなタプルをつくることになるので、負荷が高くなってしまうからです。

> To manipulate a collection of elements, use a list instead.
> (「[Tuple](https://hexdocs.pm/elixir/Tuple.html)」より)


# データの参照と取り出し
リストにはインデックスがなく、要素間の参照をたどることで値が取り出されます。つまり、最後の要素を参照するには、すべての要素を順にたどらなければならないということです。もっとも、すべての要素を列挙して処理するには差し支えなく、`Enum`モジュールの関数が使えます。

タプルの要素は、[`elem/2`](https://hexdocs.pm/elixir/Kernel.html#elem/2)関数により、インデックスを与えてただちに取り出せます。ただし、`Enum`モジュールの関数は使えません。

```elixir
iex> tuple = {:ok, "hello"}
{:ok, "hello"}
iex> elem(tuple, 1)
"hello"
```


# 適した使い途
リストは、要素を自由に操作でき、メモリの許すかぎり加えられます。タプルは、予め決まったデータのやり取りに向くでしょう。

データを操作したり、数が変わったり、とくに多くの数のデータを扱うにはリストが使われます。1件のデータの複数の属性値を表すときは、タプルが適しているでしょう。典型は、キーと値の組みです。関数の戻り値によく用いられます。たとえば、[`File.read/1`](https://hexdocs.pm/elixir/File.html#read/1)は、ファイル読み込みの結果と内容をタプルで返します。

```elixir
iex> File.read("path/to/existing/file")
{:ok, "... ファイルの中身 ..."}
iex> File.read("path/to/unknown/file")
{:error, :enoent}
```

また、タプルはパターンマッチングにも用いられます。

```elixir
iex> tuple = {:ok, :example}
{:ok, :example}
iex> {:ok, atom} = tuple
{:ok, :example}
iex> atom
:example
```

属性の数が増えてくると、タプルよりマップの方がわかりやすいかもしれません。

```elixir
iex> {100, 150, 50}
{100, 150, 50}
iex> %{x: 100, y: 150, z: 50}
%{x: 100, y: 150, z: 50}
```

リストとタプルを組み合わせることもできます。キーワードリストがまさにその例です(「[Elixir入門 07: キーワードリストとマップ](https://dev.to/gumi/elixir-07--39hi)」参照)。

```elixir
iex> point = [x: 100, y: 150, z: 50]
[x: 100, y: 150, z: 50]
iex> point == [{:x, 100}, {:y, 150}, {:z, 50}]
true
```

# まとめ
- リストもタプルも任意の型のデータを複数納められます。
- リストとタプルの仕組みは異なるので、つぎの点で違いがあります。
    - サイズ
    - 要素の操作
    - データの参照
- リストとタプルそれぞれの性質にあった使い途があります。
    - リスト: 要素を操作し、数が変わるデータ
    - タプル: 複数の属性をもった固定データ