1_8.基本問題2 - ohr486/ErlangElixirFestHandsOn GitHub Wiki

1_8.基本問題2

基本問題1の関数化

毎回、コンソールで処理を入力するのは手間ですので、以下の様に関数化します。

defmodule モジュール do
  def 関数(引数) do
    関数の処理
  end
end

基本問題1をlib/basic.exの関数q1として以下の様に実装します。

defmodule Basic do
〜略〜

  def q1 do
    # ファイルの読み込み
    data = File.stream!("data.json")

    # JSON文字列をmapに変換
    map_data = Enum.map(data, fn d -> Poison.decode!(d) end)

    # age <= 20 でフィルタリング
    filter_data = Enum.filter(map_data, fn d -> d["age"] <= 20 end)

    # 件数を取得
    Enum.count(filter_data)
  end
end

elixirはパイプ演算子(|>)を使って、関数の結果を次の関数の第一引数として受け渡せます。

r1 = func1(arg1, arg2)
r2 = func2(r1, arg3)

arg1
|> func1(arg2)
|> func2(arg3)

とかけます。 関数q1をパイプ演算子を使って記述すると

def q1 do
  "data.json"
  |> File.stream!
  |> Enum.map(fn d -> Poison.decode!(d) end)
  |> Enum.filter(fn d -> d["age"] <= 20 end)
  |> Enum.count
end

とかけます。

Streamを使った実装(遅延処理)

Enum.mapStream.mapに差し替えれば遅延処理になります。

def q2_1 do
  "data.json"
  |> File.stream!
  |> Stream.map(fn d -> Poison.decode!(d) end)
  |> Stream.filter(fn d -> d["age"] <= 20 end)
  |> Enum.count
end

Flowを使った実装(並列処理)

Flowを使った並列化は

データソース
|> Flow.from_enumerable # これを追加
|> Flow.xxx # Enum/Stream -> Flow に変更
|> Flow.partition # これを追加
|> xxx

とすることで並列化できます。

def q2_2 do
  "data.json"
  |> File.stream!
  |> Flow.from_enumerable
  |> Flow.map(fn d -> Poison.decode!(d) end)
  |> Flow.partition
  |> Flow.filter(fn d -> d["age"] <= 20 end)
  |> Enum.count
end

Flowはライブラリとなっていますので、mix.exsに下記のように記載を行い mix deps.get にてライブラリを取得してください。

defp deps do
  [
    {:poison, "~> 3.1"},
    {:flow, "~> 0.13"}
  ]
end