Comprehensions(内包表記)の使い方
ElixirのComprehensionsは
- Generators
- Filters
- Collectables
の3つで構成されている。
> for n <- [1, 2, 3, 4], do: n * n
[1, 4, 9, 16]
上記の場合、n <- [1, 2, 3, 4]
がGenerators
になる。右辺に任意のenumerable
を渡すことが可能。
例えば、
> for n <- 1..4, do: n * n
[1, 4, 9, 16]
とすることも出来る。
また、Generatorsはパターンマッチングもサポートしているので、
> values = [good: 1, good: 2, bad: 3, good: 4]
> for {:good, n} <- values, do: n * n
[1, 4, 16]
のように書ける。
パターンマッチングではなく、Filtersを使って同等の事が出来る。
> values = [good: 1, good: 2, bad: 3, good: 4]
> for {key, n} <- values, key == :good, do: n * n
[1, 4, 16]
> require Integer
> for n <- 1..4,Integer.is_odd(n), do: n * n
[1, 9]
Filtersは、nilまたはfalseの値を除外対象とする。
> require Integer
> for n <- 1..4, nil, do: n * n
[]
以下の様に複数のGeneratorsを指定出来る。
> for dir <- ['.'],
> file <- File.ls!(dir),
> path = Path.join(dir, file),
> File.regular?(path) do
> path
> end
["./.gitignore", "./mix.exs", "./README.md"]
Bitstringも同様に扱える。
> pixels = <<213, 45, 132, 64, 76, 32, 76, 0, 0, 234, 32, 15>>
> for <<r::8, g::8, b::8 <- pixels>>, do: {r, g, b}
[{213,45,132},{64,76,32},{76,0,0},{234,32,15}]
パターンマッチングもFilters使える。
> for <<r::8, g::8, b::8 <- pixels>>, r==213, do: {r, g, b}
[{213, 45, 132}]
> for <<213::8, g::8, b::8 <- pixels>>, do: {213, g, b}
[{213, 45, 132}]
Intoの使い方
上記の、Comprehensions(内包表記)は、結果をリストとして返しているが、
:into
を使用すと、異なるデータ構造にデータを登録する事が可能。
> for <<c <- " hello world ">>, c != ?\s, into: "", do: <<c>>
"helloworld"
:into
にはCollectable protocolを実装しているデータ構造であれば指定が可能。
1.0.0-rc2
では、List / BitString / Map / HashDict / HashSet / IO(IO.Stream)が実装済
List
> box = [100,200]
> for n <- [1,2,3,4,5], into: box, do: n + 1
[100, 200, 2, 3, 4, 5, 6]
BitString
> for <<c <- " hello world ">>, c != ?\s, into: "", do: <<c>>
"helloworld"
Map
> for {key,value} <- [app: "elixir", version: "0.15.2-dev"],
> into: %{} do
> {key, value: value}
> end
%{app: [value: "elixir"], version: [value: "0.15.2-dev"]}
%{}
はMap.new
にも置き換え可能。
MapはDictモジュール
に処理を委譲しているだけなので、HashDict
/HashSet
も同じように記述出来る。
%{}
の部分がHashDict.new
またはHashSet.new
になる。
IO.Stream
> stream = IO.stream(:stdio, :line)
> for line <- stream, into: stream do
> String.upcase(line) <> "\n"
> end
> for x <- ~w{ cat dog }, into: IO.stream(:stdio,:line), do: "<<#{x}>>\n"