凜「今回はパターンマッチングにゃ!」
海未「ちゃんと調べてきましたか?」
凜「ばっちりにゃ!」
希「お、自信たっぷりやね」
凜「それじゃ、行っくにゃー!」
希「にゃー!」
海未「・・・に、にゃー」
凜「えーっとね、Elixirには=
って演算子があって、左右の値が同じかどうかを比較できるにゃ」
海未「前回希が説明してくれた==
や===
とは違うのですか?」
凜「すんごく紛らわしいんだけど、違うんだよ。見て見て」
"rin" == "rin" # true
"rin" = "rin" # "rin"
"rin" = "umi" # ** (MatchError) no match of right hand side value: umi
凜「違いその1。==
だと結果はbooleanだけど、=
だと左右の値そのものになって、一致しないとエラー吐くにゃ」
希「これだけだと、なんだかようわからんけど・・・」
凜「でね、これに変数を使うと・・・」
idol = "rin" # "rin"
IO.puts idol # "rin"
凜「違いその2。左辺が変数だと右辺の値が代入されるにゃ」
希「まあ、ここだけ見ると普通やね」
海未「普通・・・?」
希「こっちの話や」
idol = "rin" # "rin"
"rin" = idol # "rin"
凜「こうすると普通じゃないにゃ」
海未「ですが、これは分かります。左右は一致していますからね」
凜「なんか比較と代入が同時にできる便利演算子だと思えばいいにゃ」
希「左右をマッチングする、ってことなんやろうけど、パターンってどういうことなん?」
凜「今は1つの値同士しかマッチングしてないけど、もっと複雑なデータ構造にマッチさせたりもできるんだよ。そのへんを見ていくと、パターンっぽいな、ってなるにゃ」
凜「ちょっとだけ難しくするね。タプルをマッチさせると・・・」
{rin, umi, nozomi} = {"hoshizora", "sonoda", "tojo"} # {"hoshizora", "sonoda", "tojo"}
{rin, umi, nozomi} = {"hoshizora", "sonoda", "tojo", "yazawa"} # ** (MatchError) no match of right hand side value: {"hoshizora", "sonoda", "tojo", "yazawa"}
凜「タプルの要素数が合っていれば代入、合ってなければエラーにゃ」
{rin, umi, nozomi} = ["hoshizora", "sonoda", "tojo"] # ** (MatchError) no match of right hand side value: ["hoshizora", "sonoda", "tojo"]
凜「要素数が合ってても型が違うとだめにゃ」
希「型とか要素数とかのパターンが一致してれば代入できる、ってことやね」
凜「うん。それと、左辺は変数ばかりとは限らないにゃ」
{:kosaka, name} = {:kosaka, "honoka"} # {:kosaka, "honoka"}
{:kosaka, name} = {:kosaka, "yukiho"} # {:kosaka, "yukiho"}
{:kosaka, name} = {:minami, "kotori"} # ** (MatchError) no match of right hand side value: {:minami, "kotori"}
凜「左辺のタプルの1個目に:kosakaって値があるから、右辺の1個目も:kosakaでないとだめにゃ」
希「これもパターンの一部なんやね」
凜「リストにはhd
とかtl
とかあったよね。|
を使ってあんな感じのマッチもできるよ」
[head | tail] = ["rin", "umi", "nozomi"]
IO.puts head # "rin"
IO.puts tail # ["umi", "nozomi"]
凜「こういうのはリストの分解を狙った使い方にゃ」
海未「=
ひとつでいろいろできる、まさに便利演算子ですね」
凜「左辺で同じ変数を何度か使うのもできるんだけど」
{name, name} = {"rin", "rin"} # {"rin", "rin"}
{name, name} = {"rin", "pana"} # ** (MatchError) no match of right hand side value: {"rin", "pana"}
凜「同じ変数に違う値は入れられないにゃ」
希「変数展開すると{"rin", "rin"} = {"rin", "pana"}
やからマッチしないってことやね」
凜「なんだけど・・・」
name = "rin"
{name, name} = {"rin", "pana"} # ** (MatchError) no match of right hand side value: {"rin", "pana"}
{^name, name} = {"rin", "pana"} # {"rin", "pana"}
凜「^
をつけるとまた話が変わってくるにゃ」
海未「・・・難しくなってきました」
凜「えへーん、そうであろう、そうであろう」
希「ここぞとばかりに調子に乗っとるな」
凜「^
はピン演算子というにゃ。Elixirの変数は、基本的に、さ、再束縛可能だけど、ピン演算子を使うと、えーと、再束縛されなくなって、元の値にマッチ・・・」
希「あかん」
海未「凜、カンペは堂々と見てもいいのですよ」
凜「凜だってちょっとくらい見栄張りたいにゃ!」
凜「・・・とにかく、そういうことにゃ」
海未「^name
は代入の対象にならずに"rin"とマッチするのですね」
希「代入じゃなくて比較だけを目的とする場合に使う感じやね」
凜「・・・海未ちゃんも希ちゃんも頭いいからすぐわかるにゃ」
海未「凜のおかげですよ」
凜「ふぇっ!?」
希「せやな。凜ちゃんが頑張っていろいろ調べてきたおかげや」
凜「・・・うん♪」
凜「もうちょっとだけ続くのにゃ」
希「壮大な続編が待ってる系やね」
凜「ううん、ほんとにちょっとにゃ」
凜「_
っていうのがあるにゃ」
{_, _, name} = {"rin", "umi", "nozomi"} # {"rin", "umi", "nozomi"}
IO.puts name # "nozomi"
IO.puts _ # ** (CompileError) iex:3: unbound variable _
凜「何にでもマッチするから、パターンの一部を無視したいときなんかに使うといいよ」
海未「本当に値は捨てられてしまうのですね」
凜「変数じゃないから後から参照はできないにゃ」
凜「これがElixirのパターンマッチングにゃ」
希「他の言語じゃあんまり見かけん仕組みやね」
海未「希、以前から気になっていたのですが・・・その妙な知識はどこから?」
希「Elixir始めてから、いろんな人と話してるんよ。この前も、Donald Knuthの守護霊と」
凜「それやばいやつにゃ!」
海未「希!早く引き返してください!」