2015/08/06のmaster(v1.1.0-dev)で動作確認
Enumerables
Enumモジュール
を使う。
> Enum.map([1, 2, 3], fn x -> x * 2 end)
[2, 4, 6]
> Enum.map(%{1 => 2, 3 => 4}, fn {k, v} -> k * v end)
[2, 12]
> Enum.map(1..3, fn x -> x * 2 end)
[2, 4, 6]
> Enum.reduce(1..3, 0, &+/2)
6
少し調べた感じでは、
- List
- Map
- Function
- GenEvent.Stream
- HashDict
- HashSet
- Range
- Stream
がProtocols
を使ってEnumerable
を実装している。
Enumerables -> Streams
Enumモジュールの殆どの関数はEnumerableを引数にとり、戻り値はlistを期待している。
> odd? = &(rem(&1, 2) != 0)
#Function<6.90072148/1 in :erl_eval.expr/5>
> Enum.filter(1..3, odd?)
[1, 3]
上記は、中間リストを生成している。
> 1..100_000 |> Enum.map(&(&1 * 3)) |> Enum.filter(odd?) |> Enum.sum
7500000000
上記の場合は、100_000のリストをEnum.mapに渡し、3倍したあと、Enum.filterで奇数のみ取得して、最後にEnum.sumを行っている。
Lazy operationsをサポートしているStreamモジュールを使うと以下のように書ける。
> 1..100_000 |> Stream.map(&(&1 * 3)) |> Stream.filter(odd?) |> Enum.sum
7500000000
上記は中間リストを生成する代わりに、ストリームを生成する。
毎回リストを作る事をしないので大量のデータを扱う場合、または無限な値を扱うのに適している。
実際にEnum.map/Enum.filter
をStream.map/Stream.filter
に変えてみたけど、劇的には変わらなかった。
例が悪いのかな??
と思ったけど、以下の場合は処理速度が全然違う。一つ目が 20秒 二つ目が 0.00008秒
> Enum.map(1..10_000_000, &(&1+1)) |> Enum.take(5)
[2, 3, 4, 5, 6]
> Stream.map(1..10_000_000, &(&1+1)) |> Enum.take(5)
[2, 3, 4, 5, 6]
-
Streamモジュール
の殆どは、enumerableを引数のとり、streamデータ型を返す関数となっている。 -
Streamモジュール
は無限値(streamデータ型)を得る事が出来る関数が備わっている。-
例)
Stream.cycle/1
で得られるstreamをEnum.mapに渡すと無限ループする。> stream = Stream.cycle([1, 2, 3]) #Function<44.83110278/2 in Stream.unfold/2> > Enum.take(stream, 10) [1, 2, 3, 1, 2, 3, 1, 2, 3, 1] > Enum.map stream, fn(x) -> x * x end 結果が返ってこない。
-
あとは、
Stream.unfold/2
Stream.resource/3
など、色々な関数が存在する。使い方はドキュメントを見た方が早い。
あとは
iex> h Stream.unfold
とかで調べる。あーでも@spec
の情報が無いからドキュメント見た方がいいか。
他の使い方
> Stream.map([1, 3, 5, 7], &(&1 + 1)) |> Enum.to_list
[2, 4, 6, 8]