Elixirのチートシートを作ろう #13 関数その3

# 1. OverView

だんだん、チートシート書いてんだか、解説してんだか、Elixir Schoolの無断転載してんのかわからなくなってきましたｗ

本日はここからやります。
https://elixirschool.com/ja/lessons/basics/functions#%E3%83%97%E3%83%A9%E3%82%A4%E3%83%99%E3%83%BC%E3%83%88%E9%96%A2%E6%95%B0-5



# 2 まずは解説
## 2.1 プライベート関数

```
他のモジュールから特定の関数へアクセスさせたくない時には関数をプライベートにすることができます。 
```

お説教はおいておくと、「他人に使われたくない関数」と言うのは結構ありまして、そう言う時に使うもんだとご理解ください。
さて、サンプル
```
defmodule Greeter do
  def hello(name), do: phrase() <> name
  defp phrase, do: "Hello, "
end
```
defで定義されている関数 pharase/0が、プライベート関数です。
実行してみましょう、hello/1は実行出来ても、phrase/0が実行できていないのがわかります。

```
iex(36)> Greeter.hello("Sean")
"Hello, Sean"
iex(37)> Greeter.phrase
** (UndefinedFunctionError) function Greeter.phrase/0 is undefined or private
    Greeter.phrase()
    iex:37: (file)
iex(37)>
```

…をを！シンプルに説明が終わったぞ！…丸写しだもんな。

## 2.2 ガード節

ガード節は、関数のパターンマッチングにおいて、「追加の条件を指定するために使用されます」。
ガード節を使うことで、関数の引数が特定の条件を満たす場合にのみ、その関数が実行されるように制御できます。
when句を指定し、「追加の条件」を追加しております。


では、サンプルを見てみましょう
```
defmodule Greeter do
  def hello(names) when is_list(names) do
    names = Enum.join(names, ", ")
    
    hello(names)
  end

  def hello(name) when is_binary(name) do
    phrase() <> name
  end

  defp phrase, do: "Hello, "
end
```

さて、実行してみましょう。
```
iex(2)> Greeter.hello ["Sean", "Steve"]
"Hello, Sean, Steve"
iex(3)> "Hello, Sean, Steve"
"Hello, Sean, Steve"
iex(4)>
```

ちゃんとwhen句が動き、どちらも同じhello/1にも関わらず、def hello(names) when is_list(names)とっても再帰が簡単ですね
def hello(name) when is_binary(name) で定義された関数が、呼び分けられてますね。

では、昨日のサンプルを「改良」してみましょう。
```
defmodule Greeter1 do
  def hello(%{name: person_name}) do
    IO.puts "Hello, " <> person_name
  end
  def hello(%{age: person_age}) do
    IO.puts "Hello, " <> person_age <> " aged person!"
  end
end
```
こいつに、when句で条件を指定してみましょう。
  def hello(%{name: person_name}) do -> こちらは、引数のkeyにname:があった場合に
  def hello(%{age: person_age}) do　-> こちらは、引数のkeyにname:がなかった場合に

```
defmodule Greeter1 do
  def hello(%{name: person_name} = target) when is_map_key(target, :name) do
    IO.puts "Hello, " <> person_name
  end
  def hello(%{age: person_age} = target) when not is_map_key(target, :name) do
    IO.puts "Hello, " <> Integer.to_string(person_age) <> " aged person!"
  end
end
```



これがテストデータです。
```
fred = %{
name: "Fred",
age: "95",
favorite_color: "Taupe"
}
```

実行結果。
見事に、使い分けられております。

```
iex(3)> Greeter1.hello(fred)
Hello, Fred
:ok
iex(4)> Greeter1.hello(%{age: 95, favorite_color: "Taupe"})
Hello, 95 aged person!
:ok
```

## 2.3 デフォルト引数

引数にデフォルトが設定出来ます。

https://elixirschool.com/ja/lessons/basics/functions#%E3%83%87%E3%83%95%E3%82%A9%E3%83%AB%E3%83%88%E5%BC%95%E6%95%B0-7


引数 \\ デフォルト値の記法を用います。
うん、用いてくれるそうです。
```
defmodule Greeter do
  def hello(name, language_code \\ "en") do
    phrase(language_code) <> name
  end

  defp phrase("en"), do: "Hello, "
  defp phrase("es"), do: "Hola, "
end
```

実行してみましょう。
```
iex(10)> Greeter.hello("Sean", "en")
"Hello, Sean"
iex(11)> Greeter.hello("Sean")
"Hello, Sean"
iex(12)> Greeter.hello("Sean", "es")
"Hola, Sean"
```

初期値が見事設定されてますねぇ。
なので、hello/1としてもマッチしております。

### 2.3 デフォルト引数の続き

さて、Elixir Schoolで、「ガードの例をデフォルト引数と組み合わせると、問題にぶつかります。 どんな風になるか見てみましょう」
と問題にぶつかっております。

```
defmodule Greeter do
  def hello(names, language_code \\ "en") when is_list(names) do
    names = Enum.join(names, ", ")
    
    hello(names, language_code)
  end

  def hello(name, language_code \\ "en") when is_binary(name) do
    phrase(language_code) <> name
  end

  defp phrase("en"), do: "Hello, "
  defp phrase("es"), do: "Hola, "
end
```

エラーはこちら
```
warning: redefining module Greeter (current version defined in memory)
  iex:16: Greeter (module)

error: def hello/2 defines defaults multiple times. Elixir allows defaults to be declared once per definition. Instead of:

    def foo(:first_clause, b \\ :default) do ... end
    def foo(:second_clause, b \\ :default) do ... end

one should write:

    def foo(a, b \\ :default)
    def foo(:first_clause, b) do ... end
    def foo(:second_clause, b) do ... end

  iex:23

** (CompileError) cannot compile code (errors have been logged)

```

def hello/2 defines defaults multiple times. Elixir allows defaults to be declared once per definition.

hello/2にといて、defaultsを複数定義しているElixirは一回しか定義しちゃだめよ、だそうです。
作例まで書いてくれてますね。すっげー便利。修正してみましょう。
「これに対処するには、デフォルト引数付きの関数を先頭に追加します」

```
defmodule Greeter do
  def hello(names, language_code \\ "en")
  
  def hello(names, language_code) when is_list(names) do
    names = Enum.join(names, ", ")
    
    hello(names, language_code)
  end

  def hello(name, language_code) when is_binary(name) do
    phrase(language_code) <> name
  end

  defp phrase("en"), do: "Hello, "
  defp phrase("es"), do: "Hola, "
end
```
実行してみましょう。
```
iex(18)> Greeter.hello ["Sean", "Steve"]
"Hello, Sean, Steve"
iex(19)> Greeter.hello ["Sean", "Steve"], "es"
"Hola, Sean, Steve"
```

めでたしめでたし？

# 3. 本日のチートシート

  |関数について注意事項|説明|例|
  |:--------|:----------------------------------------|:------------------------------------------------------------------|
  |プライベート関数|他のモジュールから特定の関数へアクセスさせたくない時には関数をプライベートに出来る|defmodule Greeter do<br>  defp phrase, do: "Hello, "<br>end<br>iex(37)> Greeter.phrase<br>エラーとなる<br>|
  |ガード節|ガード節は、関数のパターンマッチングにおいて、追加の条件を指定するために使用されます|defmodule Greeter1 do<br>  def hello(%{name: person_name} = target) when is_map_key(target, :name) do<br>    IO.puts "Hello, " <> person_name<br>  end<br>  def hello(%{age: person_age} = target) when not is_map_key(target, :name) do<br>    IO.puts "Hello, " <> Integer.to_string(person_age) <> " aged person!"<br>  end<br>end<br>|
　|デフォルト引数|引数にデフォルトが設定出来ます。引数 \\ デフォルト値で指定します|def hello(names, language_code \\ "en") when is_list(names) do<br>    names = Enum.join(names, ", ")<br>     hello(names, language_code)<br>  end<br>|