Part11:パターンマッチング

凜「今回はパターンマッチングにゃ!」
海未「ちゃんと調べてきましたか?」
凜「ばっちりにゃ!」
希「お、自信たっぷりやね」
凜「それじゃ、行っくにゃー!」
希「にゃー!」
海未「・・・に、にゃー」

凜「えーっとね、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の守護霊と」
凜「それやばいやつにゃ!」
海未「希!早く引き返してください!」


LINEで送る
Pocket


コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

次のHTML タグと属性が使えます: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">