もやもやエンジニア

IT系のネタで思ったことや技術系のネタを備忘録的に綴っていきます。フロント率高め。

Elixir 入門 その9 - Enumerables and Streams

Enumerables

  • ElixirはEnumerablesなデータを処理するのにEnumモジュールを提供している
  • EnumはListやMapなど異なるデータ型を扱うように設計されている
    • なのでデータによって特有の操作、例えばListの特定の位置に要素を追加したいというような場合はListモジュールのinsert_at/3を使う必要がある。
# Listの各要素を * 2 する
iex> Enum.map([1, 2, 3], fn x -> x * 2 end)
[2, 4, 6]

# mapの各要素を key * value する
iex> Enum.map(%{1 => 2, 3 => 4}, fn {k, v} -> k * v end)
[2, 12]

# List内の要素の合計を出す
iex> Enum.reduce(1..3, 0, &+/2)
6

The pipe operator

  • |> で関数の呼び出し結果をつなげることができる。左辺の関数の実行結果を右辺の第一引数として渡す。
# List内の各要素を3倍して、その中から奇数の合計を求める処理
# |> を使わないで書いた場合
iex> Enum.sum(Enum.filter(Enum.map(1..100, &(&1 * 3)), &(rem(&1, 2) != 0)))
7500

# |> を使って書いた場合
iex> 1..100 |> Enum.map(&(&1 * 3)) |> Enum.filter(&(rem(&1, 2) != 0)) |> Enum.sum
7500

# |> を使うとListに対してやりたいことが宣言的に書けてみやすい。

Streams

  • Enumの代わりに遅延評価をサポートするStreamモジュールが提供されている
1..100 |> Stream.map(&(&1 * 3)) |> Stream.filter(&(rem(&1, 2) != 0)) |> Enum.sum
  • Streamモジュールの多くの関数はenumerableな引数を受け付け、Streamを返す
  • 使い方によっては無限列のStreamを返す
# [1,2,3]のサイクルが続くStreamを束縛。この時点でのstreamは1,2,3を繰り返すStream
stream = Stream.cycle([1, 2, 3])

# take で streamから必要な個数を取り出す時点で初めてstreamの関数が評価される
iex> Enum.take(stream, 10)
[1, 2, 3, 1, 2, 3, 1, 2, 3, 1]
  • 遅延評価が有効 = 必要になった際に初めて評価されて展開されるので巨大なデータを扱うことに向いている。