5
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ElixirAdvent Calendar 2024

Day 19

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

Last updated at Posted at 2024-12-18

1. OverView

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

本日はここからやります。
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 デフォルト引数

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

引数 \ デフォルト値の記法を用います。
うん、用いてくれるそうです。

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
defp phrase, do: "Hello, "
end
iex(37)> Greeter.phrase
エラーとなる
ガード節 ガード節は、関数のパターンマッチングにおいて、追加の条件を指定するために使用されます 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
  デフォルト引数 引数にデフォルトが設定出来ます。引数 \ デフォルト値で指定します
5
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?