はじめに
- ある条件に合致するときは"Yes"、そうではない場合は"No"を返しなさいという関数をつくりたいとします
- AtCoderの問題にあったりします
- いろいろな書き方があるおもいますが、マイブームはpipe operator|>を使った
|> if(do: "Yes", else: "No")
です - 以降、この記事ではリストの中身が100より小さいときは"Yes"、そうではないときには"No"とします
マイブーム
iex> [1, 2, 3] |> Enum.all?(& &1 < 100) |> if(do: "Yes", else: "No")
"Yes"
iex> [1, 2, 101] |> Enum.all?(& &1 < 100) |> if(do: "Yes", else: "No")
"No"
パターンマッチ
defmodule Awesome do
def solve(list), do: Enum.all?(list, & &1 < 100) |> do_solve()
defp do_solve(true), do: "Yes"
defp do_solve(false), do: "No"
end
if文っぽく
defmodule Awesome do
def solve(list) do
if Enum.all?(list, & &1 < 100) do
"Yes"
else
"No"
end
end
end
無名関数を作ってpipe operatorでつなぐ
defmodule Awesome do
def solve(list) do
Enum.all?(list, & &1 < 100)
|> (fn b -> if b, do: "Yes", else: "No" end).()
end
end
- 私は長らくこの書き方をしていたのですが、なにか変だぞとおもって
|> if(do: "Yes", else: "No")
に気づいたわけです - 余談ですが、このテクニックはどなたかの記事で知りまして、前の計算結果を次の関数の第一引数ではない箇所に渡して呼び出したいときに重宝しています
Awesome.get_content() # .wavデータが取得できるすんごい関数
|> (fn content -> File.write("alarm.wav", content) end).()
ifって何ですか?
- マクロです
- リンク先の公式ドキュメントのサンプルをみていただけると感じるものがあるとおもいます
- もっとifのことを知りたい方は、@piacerex さんの記事をご参照ください
- Elixirのifはタダの関数
- ifをパイプの中で使用する
- いつもありがとうございます!
- Elixirのソースコード
defmacro if(condition, clauses) do
build_if(condition, clauses)
end
defp build_if(condition, do: do_clause) do
build_if(condition, do: do_clause, else: nil)
end
defp build_if(condition, do: do_clause, else: else_clause) do
optimize_boolean(
quote do
case unquote(condition) do
x when :"Elixir.Kernel".in(x, [false, nil]) -> unquote(else_clause)
_ -> unquote(do_clause)
end
end
)
end
defp build_if(_condition, _arguments) do
raise ArgumentError,
"invalid or duplicate keys for if, only \"do\" and an optional \"else\" are permitted"
end
2021/01/11(月) 追記
- @pojiro さんの書き込みにてcase/2も同じようにいけます
- パイプラインでcaseを使えることを知りました。(phx.gen.authのコードから抜粋
Wrapping Up
- Enjoy Elixir !!!