プログラミング
programming
Elixir

Elixir: 列挙型データから値を探すEnumモジュールの関数

Enumは列挙型(enumerable)データを扱うモジュールです。Enumの関数はさまざまな列挙可能なデータ型に使える多態性をもちます。扱うデータはEnumerableプロトコルを実装していればよいのです(「Elixir入門 10: EnumとStream」参照)。それらの関数の中から、値を探す3つの関数をご紹介します。とくに、Enum.find_value/3については、公式ドキュメントよりわかりやすいコード例を示しました。


Enum.find/3

Enum.find/3は、第3引数funの戻り値がtrueと評価される最初の要素を返します。関数が引数に受け取るのは、取り出される各要素です。


find(enumerable, default \\ nil, fun)

find(t(), default(), (element() -> any())) :: element() | default()

第2引数はデフォルト値です。省くとnilが与えられます。

iex> Enum.find([1, 2, 3], fn x -> rem(x, 2) == 0 end)

2
iex> Enum.find([1, 2, 3], fn x -> rem(x, 4) == 0 end)
nil
iex> Enum.find([1, 2, 3], 0, fn x -> rem(x, 4) == 0 end)
0


Enum.find_index/2

Enum.find_index/2は、第2引数funの戻り値がtrueと評価される最初の要素のインデックスを返します。関数が引数に受け取るのは、取り出される各要素です。


find_index(enumerable, fun)

find_index(t(), (element() -> any())) :: non_neg_integer() | nil

インデックスは0から始まる連番整数です。いずれの要素も、第2引数の関数funtrueと評価される値を返さないときはnilとなります。

iex> Enum.find_index([1, 2, 3], fn x -> rem(x, 2) == 0 end)

1
iex> Enum.find_index([1, 2, 3], fn x -> rem(x, 4) == 0 end)
nil


Enum.find_value/3

Enum.find_value/3は、第3引数funの戻り値がtrueと評価される最初の要素について、戻り値そのものを返します。関数が引数に受け取るのは、取り出される各要素です。


find_value(enumerable, default \\ nil, fun)

find_value(t(), any(), (element() -> any())) :: any() | nil

第2引数はデフォルト値です。省くとnilが与えられます。

iex> Enum.find_value([1, 2, 3], fn x -> rem(x, 2) == 0 end)

true
iex> Enum.find_value([1, 2, 3], fn x -> rem(x, 4) == 0 end)
nil
iex> Enum.find_value([1, 2, 3], &is_number/1)
true
iex> Enum.find_value([1, 2, 3], "no bools!", &is_boolean/1)
"no bools!"
iex> Enum.find_value([1, 2, 3], fn x ->
...> case rem(x, 2) do
...> 0 -> {:even, x}
...> _ -> false
...> end
...> end)
{:even, 2}

公式ドキュメントのEnum.find_value/3の項には、使い途の浮かぶ例が掲げられていないようです。たとえば、つぎのようにキーワードリストから値を取り出すことが考えられます。

iex> profile = [{company: "gumi, inc"}, {headquarters: "tokyo/japan"}]

iex> Enum.find_value(profile, fn {key, value} ->
...> case key do
...> :headquarters -> value
...> _ -> false
...> end
...> end)
"tokyo/japan"

この例は、and/2を用いるともっと短く書けます。

iex> Enum.find_value(profile, fn {key, value} -> key == :headquarters and value end)

"tokyo/japan"