パイプ演算子|>
については、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
Top comments (0)