LoginSignup
9
2

More than 5 years have passed since last update.

Elixir: パイプ演算子|>について

Posted at

パイプ演算子|>については、gumi TECH Blog「Elixir入門 10: EnumとStream」の「パイプ演算子」で説明されています。本稿は、そのおさらいもしつつ、公式ドキュメントの情報などを補います。

パイプライン演算

パイプ演算子|>による演算は、パイプライン演算と呼ばれることもあります。左辺の式の値を右辺の関数の第1引数として渡します。

左辺 |> 右辺

たとえば、つぎのコードはList.flatten/1でリスト内の入れ子を平坦化します。左辺が第1引数ですので、右辺の関数の引数には加えません。

iex> list = [1, [[2], 3]] |> List.flatten 
[1, 2, 3]

引数がふたつあるときは、関数には第2引数をかっこ()でくくって渡します。この場合のかっこは省けません(省くと警告が示されます)。

iex> square = list |> Enum.map(fn(x) -> x * x end)      
[1, 4, 9]

引数がふたつ以上あるときも、同じように第1引数を省いて、第2引数以降をかっこ()に加えてください。

iex> sum = square |> Enum.reduce(0, fn(x, acc) -> x + acc end)
14

上記3つの演算をパイプ演算子なしに1行で書くと、つぎのようになります。入れ子になり、しかも内側から演算されますので、読みにくいコードです。

iex> sum = Enum.reduce(Enum.map(List.flatten([1, [[2], 3]]), fn(x) -> x * x end), 0, fn(x, acc) -> x + acc end)
14

パイプ演算子を使えば、左から演算の順に書けますので、わかりやすいでしょう。

iex> [1, [[2], 3]] |> List.flatten |> Enum.map(fn x -> x * x end) |> Enum.reduce(0, fn(x, acc) -> x + acc end) 

引数に関数があるときは、キャプチャ演算子&を使うとさらに短く書けます(「Elixir入門 08: モジュールと関数」「関数のキャプチャ」参照)。

iex> [1, [[2], 3]] |> List.flatten |> Enum.map(&(&1 * &1)) |> Enum.reduce(0, &+/2)
14

パイプ演算子で注意すること

Elixir公式ドキュメントの|>/2の項にはふたつの注意が示されています。

演算子の優先順位

ひとつは、演算子の優先順位に関わるかっこ()づけです。つぎのコードはエラーになります。

String.graphemes "Hello" |> Enum.reverse

演算子の優先順位にしたがってつぎのように解釈され、Enumerableプロトコルがバイナリに対して定められていないからです。

iex> String.graphemes("Hello" |> Enum.reverse)
** (Protocol.UndefinedError) protocol Enumerable not implemented for "Hello"

したがって、つぎのように優先順位をかっこで示さなければなりません。

iex> String.graphemes("Hello") |> Enum.reverse
["o", "l", "l", "e", "H"]

もっとも、せっかくパイプ演算子を使うのですから、つぎのように書くほうがよいでしょう。

iex> "Hello" |> String.graphemes |> Enum.reverse
["o", "l", "l", "e", "H"]

無名関数に用いるとき

もうひとつの注意は、パイプ演算子を無名関数に用いるときです(関数のキャプチャについても同じ)。

iex> func = fn(str) -> str |> String.graphemes |> Enum.reverse end
#Function<6.127694169/1 in :erl_eval.expr/5>

関数名のあとのドット(.)を忘れてはいけませんし、引数がひとつでもかっこ()は省けません。

iex> "Hello" |> func.()
["o", "l", "l", "e", "H"]

けれど、パイプ演算子を使わなくてもこれは同じです。

iex> func.("Hello")
["o", "l", "l", "e", "H"]

少し気をつける点はあるものの、見やすいコードを書くために、パイプ演算子は役立つでしょう。ところで、『プログラミングElixir』(原書『Programming Elixir 1.6』)の著者Dave Thomas氏はこのパイプ演算子がお気に入りのようで、書籍のサブタイトルにつぎのように使われています。

Functional
|> Concurrent
|> Pragmatic
|> Fun

9
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
2