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

gumitech

gumi TECH

Posted on February 19, 2019

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

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

iex> list = [3.14, :pie, true, "Apple"]
[3.14, :pie, true, "Apple"]
iex> tuple = {3.14, :pie, true, "Apple"}
{3.14, :pie, true, "Apple"}
Enter fullscreen mode Exit fullscreen mode

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

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

リストの仕組み

リストは「連結リスト」です。各要素は値とつぎの要素への参照をもちます。要素を特定するインデックスは備えません。

qiita_12_001_001.png

タプルの仕組み

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

qiita_12_001_002.png

サイズに関わる違い

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

サイズを得る関数は、リストがlength/1、タプルはtuple_size/1です1。つぎのような処理の違いがあります。

関数 値の求め方 処理
length/1 数えて返す 遅い
tuple_size/1 予め計算されている 速い

要素の追加や削除などの操作

リストはListのほかEnumモジュールの豊富な関数で操作できます。特定の要素を追加したり、削除したり、変更するだけでなく、すべての要素を取り出して処理できるのです。

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

To manipulate a collection of elements, use a list instead.
(「Tuple」より)

図001■Tupleに備わる関数

tuple_functions.png

データの参照と取り出し

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

タプルの要素は、elem/2関数により、インデックスを与えてただちに取り出せます。ただし、Enumモジュールの関数は使えません。

iex> tuple = {:ok, "hello"}
{:ok, "hello"}
iex> elem(tuple, 1)
"hello"
Enter fullscreen mode Exit fullscreen mode

適した使い途

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

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

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

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

iex> tuple = {:ok, :example}
{:ok, :example}
iex> {:ok, atom} = tuple
{:ok, :example}
iex> atom
:example
Enter fullscreen mode Exit fullscreen mode

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

iex> {100, 150, 50}
{100, 150, 50}
iex> %{x: 100, y: 150, z: 50}
%{x: 100, y: 150, z: 50}
Enter fullscreen mode Exit fullscreen mode

リストとタプルを組み合わせることもできます。キーワードリストがまさにその例です(「Elixir入門 07: キーワードリストとマップ」参照)。

iex> point = [x: 100, y: 150, z: 50]
[x: 100, y: 150, z: 50]
iex> point == [{:x, 100}, {:y, 150}, {:z, 50}]
true
Enter fullscreen mode Exit fullscreen mode

まとめ

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

  1. データ構造の中の要素を数える関数について、Elixirは名前のつけ方を決めています。予め計算された値を返すのがsizeのつく関数です。数えて返す関数にはlengthがつきます。長さを調べて返すと、数が増えるほど処理は遅くなるのです。 

💖 💪 🙅 🚩
gumitech
gumi TECH

Posted on February 19, 2019

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related