もやもやエンジニア

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

Elixir 入門 その16 - Comprehensions

  • Elixirではあるリストをfilterしてmapにかけて別のリストを作るということをしたい場合、Enumモジュールを使うことが一般的だが、リスト内包表記を使って書くこともできる。forを使うとEnumやStreamを使って書くより簡単な記述にできることがある。
  • (確か)Erlangの書き方に近い

Generators and filters

  • n <- [1, 2, 3, 4] のような式をgeneratorという
# for リストから取り出した値 <- リスト , do: 値を加工する式
iex> for n <- 1..4, do: n * n
[1, 4, 9, 16]
  • リストから取り出す場合、フィルタをかけることができる
# パターンマッチを使ったフィルタ
iex> values = [good: 1, good: 2, bad: 3, good: 4]
iex> for {:good, n} <- values, do: n * n
[1, 4, 16]

# マッチするものが無い場合は空で返る
iex> for {:hoge, n} <- values, do: n * n
[]

# フィルタ用の関数を作成。評価の結果がnilかfalseの場合はフィルタで取り除かれる
iex> multiple_of_3? = fn(n) -> rem(n, 3) == 0 end

# 3の倍数のものだけが取り出されている
iex> for n <- 0..5, multiple_of_3?.(n), do: n * n
[0, 9]
  • generatorとfilterは複数定義することができる。
# ディレクトリ内のファイルを取得し、各ファイルのサイズをリストにして返す例
# dirとpathにgeneratorを使って束縛し、regular fileのみを抽出している
# ファイルのパスをpathという変数に束縛しているが、スコープはやはりレキシカルで内部からのみ参照可能
for dir  <- dirs, file <- File.ls!(dir), path = Path.join(dir, file), File.regular?(path) do
  File.stat!(path).size
end
  • バイナリを扱うこともできる
# バイナリからピクセルのリストを受け取ってtupleに変換する例
iex> pixels = <<213, 45, 132, 64, 76, 32, 76, 0, 0, 234, 32, 15>>
iex> for <<r::8, g::8, b::8 <- pixels>>, do: {r, g, b}
[{213, 45, 132}, {64, 76, 32}, {76, 0, 0}, {234, 32, 15}]
  • into: オプションを使うとList以外のデータ型に変換することができる
  • into: は変換後の元データに into: で指定した値の型に変換した上で結合して返る。変換出来ない場合は例外。
# into が無いと bitのリストが返る
iex> for <<c <- " hello world ">>, c != ?\s, do: <<c>>
["h", "e", "l", "l", "o", "w", "o", "r", "l", "d"]

# into: "" をつけるとstringで返る
iex> for <<c <- " hello world ">>, c != ?\s, into: "", do: <<c>>
"helloworld"

# into: "Elixir " をつけるとElixir が先頭に結合された状態で返る
iex> for <<c <- " hello world ">>, c != ?\s, into: "Elixir ", do: <<c>>
"Elixir helloworld"

# Mapを操作する場合、keyはそのままにvalueだけ加工したい場合など便利
iex> for {key, val} <- %{"a" => 1, "b" => 2}, into: %{}, do: {key, val * val}
%{"a" => 1, "b" => 4}

# intoが無いとこうなる
iex> for {key, val} <- %{"a" => 1, "b" => 2}, do: {key, val * val}
[{"a", 1}, {"b", 4}]