1. OverView
さて、二日に分けた制御構造の続き。完走するなら、あと13日分かー。
2. まずは解説
2.1 cond
さて、 Elixir Schoolの解説を見てみましょう。
https://elixirschool.com/ja/lessons/basics/control_structures#cond-2
値ではなく、条件をマッチさせる必要がある時には、 cond/1 を使うことができます。これは他の言語でいうところの else if や elsif のようなものです:
まずはサンプルの実行
iex(14)> cond do
...(14)> 2 + 2 == 5 ->
...(14)> "This will not be true"
...(14)> 2 * 2 == 3 ->
...(14)> "Nor this"
...(14)> 1 + 1 == 2 ->
...(14)> "But this will" <-こいつだけが実行されますね。
...(14)> end
"But this will"
次
iex(15)> cond do
...(15)> 7 + 1 == 0 -> "Incorrect"
...(15)> true -> "Catch all" <-こいつが実行されております
...(15)> end
"Catch all
iex(19)> a = 1
1
iex(20)> b = 3
3
iex(21)> a + b
4
iex(22)>
nil
iex(23)> cond do
...(23)> a + b == 2 -> "OK"
...(23)> true -> "Catch all"
...(23)> end
"Catch all"
Else ifと言っているので、以下の様に
iex(31)> cond do
...(31)> a + b == 2 -> "OK,2"
...(31)> a + b == 4 -> "OK,4" -->ここがtrueになる
...(31)> true -> "Catch all" --> ここは評価されない。
...(31)> end
"OK,4"
先頭からやって、trueになると抜けるんですね。これ。
2.2 with
2.2.1 withの説明
うん、これの為にdayを分けた。
https://elixirschool.com/ja/lessons/basics/control_structures#with-3
「特殊形式の with/1 はネストされた case/2 文を使うような時やきれいにパイプできない状況に便利です。 with/1 式はキーワード, ジェネレータ, そして式から成り立っています」
後半はわかるが…文法を調べないとさっぱりわからないな。
with
pattern1 <- expression1,
pattern2 <- expression2, ...,
do:
result_expression
これ、withの後の条件が満たされた場合、「すべてが条件式を満たしたら」 do:以下のresult_expressionが実行されるらしい。
では、サンプル…理解の前にElixir Schoolの文章を読んでみましょう。
「ジェネレータについてはリスト内包表記のレッスンでより詳しく述べますが、今は <- の右側と左側を比べるのにパターンマッチが使われることを知っておくだけでよいです。」
<-の右側と左側を比べるのに、パターンマッチ、つまり、これ、関数が実行されるんですね。
なので、例は…
user = %{first: "Sean", last: "Callan"}
これとmatchする
with {:ok, first} <- Map.fetch(user, :first),
{:ok, last} <- Map.fetch(user, :last),
do: last <> ", " <> first
1行ずつ見ていくと、
Map.fetch(user, :first) こいつの結果が{:ok, "Sean"}
Map.fetch(user, :last), こいつの結果が{:ok, "Callan"}
それぞれの行がマッチするので、do:以降のresult_expressionが実行されます。
今回はresult_expressionは、以下ですね。
このサンプル、実に味があるなと思うのが、結果。
do: last <> ", " <> first
"Callan, Sean"
結果が意味深でして。
上にも書いたとおり、「今は <- の右側と左側を比べるのにパターンマッチが使われることを知っておくだけでよいです。」の通りでして。
上の二つのパターンマッチが行われているので、last, firstに値がbindされてます。
2.2.2 with/1でelseが使えます
次のサンプル
Elixir 1.3からは with/1 で else を使えます:
import Integer
m = %{a: 1, c: 3}
a =
with {:ok, number} <- Map.fetch(m, :a),
true <- is_even(number)
do
IO.puts "#{number} divided by 2 is #{div(number, 2)}"
:even
else
:error ->
IO.puts("We don't have this item in map")
:error
_ ->
IO.puts("It is odd")
:odd
end
実行結果が
It is odd
さて、細かく説明(と言うか、俺の理解が…)
{:ok, number} <- Map.fetch(m, :a) mのmapに入っているa:を取り出して、評価しております。
is_even(number) numberが偶数か奇数か判定
これ、先ほども話した通り「今は <- の右側と左側を比べるのにパターンマッチが使われることを知っておくだけでよいです」ですね。
この両方が実行されます。
elseにて、この条件に当てはまらない
elseで二つのケースのエラーハンドリングをしています。
:error ->
IO.puts("We don't have this item in map")
:error
_ ->
IO.puts("It is odd")
:odd
まずは、:error。これは、Map.fetch(m, :a) が失敗した場合ですね。
次、_で、Map.fetch(m, :a)は成功したが、is_even(number)がfalseだった場合となります。
では、実行結果を張っておきます
m = %{a: 2, c: 3}
2 divided by 2 is 1
:even
m = %{q: 2, c: 3}
We don't have this item in map
:error
3. 本日のチートシート!
制御構造の名 | 説明 | 例 |
---|---|---|
cond | 値ではなく、条件をマッチさせる | cond do 7 + 1 == 0 -> "Incorrect" true -> "Catch all" end "Catch all" |
with | withで指定された複数のパターンマッチを行い、trueであればdo以下を実行する | with {:ok, first} <- Map.fetch(user, :first), {:ok, last} <- Map.fetch(user, :last), do: last <> ", " <> first "Callan, Sean" |
4. 本日の課題.…
僕の宿題をまとめておきます。