パイプ演算子
プログラムをこれまでにある程度書いたことがあれば,関数が入れ子になって可読性が下がったという経験があることでしょう.Elixirにはそれを解決する良い演算子があります.以下の関数を例に取りましょう.
Enum.reduce(Enum.filter(Enum.map(1..10_000, &(&1 + 2)), &(rem(&1, 2) == 0)), 0, &+/2)
上記は3重の入れ子になっていて非常に読みづらくなっています.頑張って処理を読み解くと,1から1万までの数字に2を足し,その結果の偶数のみを加算していると言うことがわかります.こんなコードをレビューしてくれと言われた日には発狂する自信があります.
恐らく他言語なら各処理の結果を1つずつ変数に格納すると言った方法をとるでしょう.しかし,1回しか使わない,しかも次の行で使用する値をいちいち変数に格納するのはあまりスマートではありません.Elixirではこのようなジレンマをパイプ演算子を用いて解決することが出来ます.
1..10_000
|> Enum.map(&(&1 + 2))
|> Enum.filter(&(rem(&1, 2) == 0))
|> Enum.reduce(0, &+/2)
とても読みやすく書き直すことが出来ました.この書き方は入れ子を解決するだけで無く,データを処理する順番に関数を並べることができるというメリットも得られます.
パイプライン演算子は左辺の評価結果を右辺の関数の第1引数に与えるという挙動をします.
関数の結果のハンドリングに用いる
Elixirでは成功するかわからない関数の結果が{:ok, ...}
,{:error, ...}
として返される場合があります.成功した場合だけその結果を用いて次の処理をしたいという場合,case文を使えば簡単に書くことができます.
case func(arg) do
{:ok, result} -> next_func(result)
error -> error # {:error, ...}をerrorとしてマッチしています
end
さらに,パイプ演算子を用いてこのように書くこともできます.
func(arg)
|> case do
{:ok, result} -> next_func(result)
error -> error
end
練習問題
プログラムが長くなるので,exs
ファイルの使用をお勧めします.
Enumモジュールの章の練習問題1,2をパイプ演算子を使って1行で解いてみましょう
ヒント
Enum.reduce/3
をそのまま使うと最初か最後に余計な文字が入ってしまいます.パターンマッチを検討してみましょうiex(1)> prime_ministers |> Enum. ..... "伊藤博文 -> 黒田清隆 -> 三條實美 -> 山縣有朋 -> 松方正義"
歴代の総理大臣をさらにわかりやすく並べるために,何代目かを併記してみましょう.
ヒント
Enum.with_index/2
を使うと先頭から順番に採番をしてくれます.Enum.with_index/2
は第1引数がリスト,第2引数が数字のオフセットになっています)採番の結果をそのまま使うと
1代目
と少し変な感じになってしまいます.パターンマッチを検討してみましょうiex(2)> prime_ministers |> Enum. ..... "初代 伊藤博文 -> 2代目 黒田清隆 -> 3代目 三條實美 -> 4代目 山縣有朋 -> 5代目 松方正義"
余談:三條實美は現在の政府解釈では3代目ではないそうです.(問題のためにあえて残しました.)