今日のElixirSchoolメモ6「関数」

Elixir Schoolの学習メモです。

https://elixirschool.com/ja/lessons/basics/functions

## 匿名関数

名前を持たない関数。匿名関数はたびたび他の関数に渡される。
`fn`と`end`のキーワードが必要。

```elixir
iex(101)> sum = fn(a, b) -> a + b end
#Function<41.3316493/2 in :erl_eval.expr/6>
iex(102)> sum.(2, 3)
5
```

### &省略記法

```elixir
iex(104)> sum = &(&1 + &2)
&:erlang.+/2
iex(105)> sum.(2, 3)
5
```

省略記法では、引数を`&1`、`&2`、`&3`...として扱う。

## パターンマッチを関数に適用する

パターンマッチは関数へと適用することができる。

```elixir
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`キーワードを用いて定義する。

```elixir
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行で書くこともできる。

```elixir
defmodule Greeter do
  def hello(name), do: "Hello, " <> name
end
```

## 関数呼び出しとパターンマッチ

```elixir
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
```

## 関数の命名とアリティ

```elixir
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`となる。
他の言語におけるオーバーロードと違い、別の関数として扱われる。

パターンマッチは同じ引数を取る関数定義が複数ある場合のみ適用される。

## 関数はキーの有無でパターンマッチできる

```elixir
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
```

名前とは別にマップ全体の値もアサインしたい場合。

```elixir
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`で定義できる。モジュール自身の内部からのみ呼び出すことができる。

```elixir
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つの述語が真であるとき、その関数を実行する。

```elixir
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
```

## デフォルト引数（デフォルトパラメータ）

`パラメータ \\ 値`という構文で、どのパラメータにもデフォルトの値を設定できる。

```elixir
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"
```