Elixir Schoolの学習メモです。
匿名関数
名前を持たない関数。匿名関数はたびたび他の関数に渡される。
fn
とend
のキーワードが必要。
iex(101)> sum = fn(a, b) -> a + b end
#Function<41.3316493/2 in :erl_eval.expr/6>
iex(102)> sum.(2, 3)
5
&省略記法
iex(104)> sum = &(&1 + &2)
&:erlang.+/2
iex(105)> sum.(2, 3)
5
省略記法では、引数を&1
、&2
、&3
...として扱う。
パターンマッチを関数に適用する
パターンマッチは関数へと適用することができる。
iex(107)> handle_result = fn
...(107)> {:ok, result} -> IO.puts "Handling result..."
...(107)> {:ok, _} -> IO.puts "This would be never run as previous will be matched beforehand."
...(107)> {:error} -> IO.puts "An error has occurred!"
...(107)> end
warning: variable "result" is unused (if the variable is not meant to be used, prefix it with an underscore)
iex:108
#Function<42.3316493/1 in :erl_eval.expr/6>
iex(108)> some_result = 1
1
iex(109)> handle_result.({:ok, some_result})
Handling result...
:ok
iex(110)> handle_result.({:error})
An error has occurred!
:ok
名前付き関数
関数を名前付きで定義して後から呼び出すことができる。
名前つき関数はモジュール内部でdef
キーワードを用いて定義する。
iex(113)> defmodule Greeter do
...(113)> def hello(name) do
...(113)> "Hello, " <> name
...(113)> end
...(113)> end
{:module, Greeter,
<<70, 79, 82, 49, 0, 0, 5, 180, 66, 69, 65, 77, 65, 116, 85, 56, 0, 0, 0, 178,
0, 0, 0, 18, 14, 69, 108, 105, 120, 105, 114, 46, 71, 114, 101, 101, 116,
101, 114, 8, 95, 95, 105, 110, 102, 111, 95, ...>>, {:hello, 1}}
iex(114)> Greeter.hello("taro")
"Hello, taro"
defは1行で書くこともできる。
defmodule Greeter do
def hello(name), do: "Hello, " <> name
end
関数呼び出しとパターンマッチ
iex(116)> defmodule Length do
...(116)> def of([]), do: 0
...(116)> def of([_ | tail]), do: 1 + of(tail)
...(116)> end
{:module, Length,
<<70, 79, 82, 49, 0, 0, 5, 132, 66, 69, 65, 77, 65, 116, 85, 56, 0, 0, 0, 158,
0, 0, 0, 16, 13, 69, 108, 105, 120, 105, 114, 46, 76, 101, 110, 103, 116,
104, 8, 95, 95, 105, 110, 102, 111, 95, 95, ...>>, {:of, 1}}
iex(117)> Length.of []
0
iex(118)> Length.of [1, 2, 3]
3
関数の命名とアリティ
defmodule Greeter2 do
# hello/0
def hello(), do: "Hello, anonymous person!"
# hello/1
def hello(name), do: "Hello, " <> name
# hello/2
def hello(name1, name2), do: "Hello, #{name1} and #{name2}"
end
1つ目の実装は引数をとらないのでhello/0
、2つ目はhello/1
となる。
他の言語におけるオーバーロードと違い、別の関数として扱われる。
パターンマッチは同じ引数を取る関数定義が複数ある場合のみ適用される。
関数はキーの有無でパターンマッチできる
iex(1)> defmodule Greeter1 do
...(1)> def hello(%{name: person_name}) do
...(1)> IO.puts "Hello, " <> person_name
...(1)> end
...(1)> end
{:module, Greeter1,
<<70, 79, 82, 49, 0, 0, 6, 20, 66, 69, 65, 77, 65, 116, 85, 56, 0, 0, 0, 199,
0, 0, 0, 21, 15, 69, 108, 105, 120, 105, 114, 46, 71, 114, 101, 101, 116,
101, 114, 49, 8, 95, 95, 105, 110, 102, 111, ...>>, {:hello, 1}}
iex(2)> taro = %{ name: "taro" }
%{name: "taro"}
iex(3)> Greeter1.hello(taro)
Hello, taro
:ok
名前とは別にマップ全体の値もアサインしたい場合。
iex(6)> defmodule Greeter2 do
...(6)> def hello(%{name: person_name} = person) do
...(6)> IO.puts "Hello, " <> person_name
...(6)> IO.inspect person
...(6)> end
...(6)> end
{:module, Greeter2,
<<70, 79, 82, 49, 0, 0, 6, 112, 66, 69, 65, 77, 65, 116, 85, 56, 0, 0, 0, 207,
0, 0, 0, 22, 15, 69, 108, 105, 120, 105, 114, 46, 71, 114, 101, 101, 116,
101, 114, 50, 8, 95, 95, 105, 110, 102, 111, ...>>, {:hello, 1}}
iex(7)> taro = %{ name: "taro", age: 20, favorite_color: "Taupe"}
%{age: 20, favorite_color: "Taupe", name: "taro"}
iex(8)> Greeter2.hello(taro)
Hello, taro
%{age: 20, favorite_color: "Taupe", name: "taro"}
プライベート関数
defp
で定義できる。モジュール自身の内部からのみ呼び出すことができる。
iex(11)> defmodule Greeter do
...(11)> def hello(name), do: phrase() <> name
...(11)> defp phrase, do: "Hello, "
...(11)> end
{:module, Greeter,
<<70, 79, 82, 49, 0, 0, 5, 232, 66, 69, 65, 77, 65, 116, 85, 56, 0, 0, 0, 185,
0, 0, 0, 19, 14, 69, 108, 105, 120, 105, 114, 46, 71, 114, 101, 101, 116,
101, 114, 8, 95, 95, 105, 110, 102, 111, 95, ...>>, {:phrase, 0}}
iex(12)> Greeter.hello("Sean")
"Hello, Sean"
iex(13)> Greeter.phrase
** (UndefinedFunctionError) function Greeter.phrase/0 is undefined or private
Greeter.phrase()
ガード節
パターンマッチによって渡された引数に応じて、どの関数が実行するかを決めることができる。
引数の型や、値の評価のチェックによって区別したいときはガード節を使う。
Elixirはまず従来のパラメータベースのパターンマッチを行い、それからwhen
述語をすべて評価し、少なくとも1つの述語が真であるとき、その関数を実行する。
iex(14)> defmodule Guard do
...(14)> def what_is(x) when is_number(x) do
...(14)> IO.puts "#{x} is a number"
...(14)> end
...(14)> def what_is(x) when is_atom(x) do
...(14)> IO.puts "#{x} is a atom"
...(14)> end
...(14)> end
{:module, Guard,
<<70, 79, 82, 49, 0, 0, 6, 200, 66, 69, 65, 77, 65, 116, 85, 56, 0, 0, 0, 223,
0, 0, 0, 22, 12, 69, 108, 105, 120, 105, 114, 46, 71, 117, 97, 114, 100, 8,
95, 95, 105, 110, 102, 111, 95, 95, 10, ...>>, {:what_is, 1}}
iex(15)> Guard.what_is(:fuga)
fuga is a atom
:ok
デフォルト引数(デフォルトパラメータ)
パラメータ \\ 値
という構文で、どのパラメータにもデフォルトの値を設定できる。
iex(17)> defmodule Greeter do
...(17)> def hello(name, language_code \\ "en") do
...(17)> phrase(language_code) <> name
...(17)> end
...(17)>
...(17)> defp phrase("en"), do: "Hello, "
...(17)> defp phrase("es"), do: "Hola, "
...(17)> end
{:module, Greeter,
<<70, 79, 82, 49, 0, 0, 7, 56, 66, 69, 65, 77, 65, 116, 85, 56, 0, 0, 0, 185,
0, 0, 0, 19, 14, 69, 108, 105, 120, 105, 114, 46, 71, 114, 101, 101, 116,
101, 114, 8, 95, 95, 105, 110, 102, 111, 95, ...>>, {:phrase, 1}}
iex(19)> Greeter.hello("Sean", "en")
"Hello, Sean"
iex(20)> Greeter.hello("Sean")
"Hello, Sean"
iex(21)> Greeter.hello("Sean", "es")
"Hola, Sean"