1. OverView
さて、本日は割と順調に進んでくれると信じております。
もっとも、元の構成を入れ替えしてて、自分でもどこ書いてんだかわからんのだけどw
本日は、こちらからスタートします。
2. まずは解説
2.1 関数の命名とアリティ
意味ありげなタイトルだなぁ、今までもちょこちょこと使ってる言葉なのですが、ここでfixしましょう。
では、Elixir Schoolの文章
「以前言及したとおり、関数は名前とアリティ(引数の数)の組み合わせで命名されます」
そして、例が「つまり、以下のようなことができるということです」
defmodule Greeter2 do
def hello(), do: "Hello, anonymous person!" # hello/0
def hello(name), do: "Hello, " <> name # hello/1
def hello(name1, name2), do: "Hello, #{name1} and #{name2}" # hello/2
end
実行結果は以下の通り、待っててね、今、解説するから。
iex(2)> Greeter2.hello()
"Hello, anonymous person!"
iex(3)> Greeter2.hello("Fred")
"Hello, Fred"
iex(4)> Greeter2.hello("Fred", "Jane")
"Hello, Fred and Jane"
関数は、引数の数アリティを/2を付与してますね。
つまり、上のdefmodule Greeter2の中にある関数は、それぞれ、hello/0,hello/1,hello/2と言う
別々の関数なわけです。
なので、サンプルがそれぞれ、引数が0,1,2に対応してます。
他の言語におけるオーバーロードとは違い、これらは互いに 異なる 関数として扱われます
これ、意外と便利そうだし、今後もガンガン使いそうですね。
C言語で可変引数と言うのがあってな…みたいな昔話はやめましょうかw
2.2 関数とパターンマッチ(2.1の話の続き)
2.1の話の続きになるんですが、気になる一文
「内部では、関数は実行された時の引数をパターンマッチしています。」
さて、掘り下げてみましょう。
defmodule Greeter1 do
def hello(%{name: person_name}) do
IO.puts "Hello, " <> person_name
end
end
マップのキーがname:の奴にマッチしますね。
文中では、マップを受け取るが、特定のキーにだけ関心がある関数と呼ばれてますね。
これは、name:とパターンマッチしている様です。
テストデータを用意して、fredにbindしておきます。
fred = %{
name: "Fred",
age: "95",
favorite_color: "Taupe"
}
Greeter1.hello(fred)
"Hello, Fred"
関数helloは、map fredを受け取り、name:を使い、person_nameを出力します。
「なので」:name キーを 含まない マップで関数を実行すると、「当然」エラーなります。
Greeter1.hello(%{age: "95", favorite_color: "Taupe"})
** (FunctionClauseError) no function clause matching in Greeter1.hello/1
The following arguments were given to Greeter1.hello/1:
# 1
%{age: "95", favorite_color: "Taupe"}
iex:83: Greeter1.hello/1
iex:89: (file)
元のElixir Schoolの説明を見てみましょう。
このような挙動となる理由は、Elixirは関数が実行された際の引数を関数で定義されたアリティに対してパターンマッチさせているためです。
これ、意味深なので、サンプルを改変してみました。
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
fred = %{
name: "Fred",
age: "95",
favorite_color: "Taupe"
}
実行結果は…
iex(14)> Greeter1.hello(%{age: "95", favorite_color: "Taupe"})
Hello, 95 aged person!
:ok
iex(15)> Greeter1.hello(fred)
Hello, Fred
:ok
どちらも、Greeter1.hello/1なので、どうなるかと思いましたが、ちゃんとマッチしてくれますねぇ。
2.3 関数とパターンマッチ その2
…いやぁ、Day分けようかと思ったw
前項のキモは
def hello(%{name: person_name}) do
IO.puts "Hello, " <> person_name
end
%{name: person_name}がパターンマッチで評価されるので、値が無ければエラーになる、と言う話だったと思ってます。
では、引数のmapすべてが欲しい時は…パターンマッチを工夫してみましょう。
defmodule Greeter2 do
def hello(%{name: person_name} = person) do
IO.puts "Hello, " <> person_name
IO.inspect person
end
end
ここで注意書きが…
%{name: person_name} = person} は %{name: person_name} が person に対してパターンマッチしているように見えたとしても、
実際には それぞれが 渡された引数をパターンマッチしているということを覚えておいてください。
つまり、こういう事でしょうか?
下の例では、person2, person_name2それぞれにfredがマッチします。
iex(27)> (person2 = %{name: person_name2}) = fred
%{name: "Fred", age: "95", favorite_color: "Taupe"}
iex(28)> person2
%{name: "Fred", age: "95", favorite_color: "Taupe"}
iex(29)> person_name2
"Fred"
さて、ここでElixir Schoolを引用しておきましょう。
まとめ: 関数は渡されたデータをそれぞれの引数で独立してパターンマッチします。 関数の中で別々の変数に格納するためにこれを利用できます。
3. # 3. 本日のチートシート!
関数について注意事項 | 説明 | 例 |
---|---|---|
関数の命名とアリティ | 関数は名前とアリティ(引数の数)の組み合わせで命名されます | 以下の例では、hello/0,/1,/2が定義されている defmodule Greeter2 do def hello(), do: "Hello, anonymous person!" # hello/0 def hello(name), do: "Hello, " <> name # hello/1 def hello(name1, name2), do: "Hello, #{name1} and #{name2}" # hello/2 end |
|
|関数とパターンマッチ|関数は実行された時の引数をパターンマッチしている|以下の例では、%{name: person_name}にマッチする引数をとる
defmodule Greeter1 do
def hello(%{name: person_name}) do
IO.puts "Hello, " <> person_name
end
end
|
4. 本日の課題.…
僕の宿題をまとめておきます。