第13章. パターンマッチを深く
この章の目的
この章を読み終える頃には、matchを一通り理解しているはずです — 受け付けるパターンの種類、網羅性が必要であるというルール、そしてアームを絞り込むガード、までを知ることになります。この章を読み終える頃には、matchを一通り理解しているはずです — 受け付けるパターンの種類、網羅性が必要であるというルール、そしてアームを絞り込むガード、までを知ることになります。
おさらい
前の章ですでにmatchを使ってケースの中から選びました。第7章ではWordをリテラルの数と照合しました。第11章では列挙型をそのヴァリアントと照合しました。この章ではその全体像をまとめます。前の章ですでにmatchを使ってケースの中から選びました。第7章ではWordをリテラルの数と照合しました。第11章では列挙型をそのヴァリアントと照合しました。この章ではその全体像をまとめます。
matchは1つの値とアームの並びを持ちます。各アームはパターン、=>、そして結果、という形です。値に最初に当てはまるパターンを持つアームが実行されます。matchは1つの値とアームの並びを持ちます。各アームはパターン、=>、そして結果、という形です。値に最初に当てはまるパターンを持つアームが実行されます。
パターンの種類
アームには3種類のパターンが出てきます。
- リテラル(たとえば
3)はその値だけに当てはまります。 - 束縛(たとえば
midi)は任意の値に当てはまり、アームの中でその名前を与えます。束縛(たとえばmidi)は任意の値に当てはまり、アームの中でその名前を与えます。 - ワイルドカード
_は任意の値に当てはまり、名前は付けません。これがキャッチオールです。ワイルドカード_は任意の値に当てはまり、名前は付けません。これがキャッチオールです。
列挙型のヴァリアントパターン、たとえばSignal::Note(midi)は、一つのヴァリアントに当てはまり、そのヴァリアントが持ち運ぶ値を束縛します。列挙型のヴァリアントパターン、たとえばSignal::Note(midi)は、一つのヴァリアントに当てはまり、そのヴァリアントが持ち運ぶ値を束縛します。
動く例
enum Signal {
Rest,
Note(Word),
}
fn loudness(s: Signal) -> Word {
match s {
Signal::Rest => 0,
Signal::Note(midi) when midi >= 60 => 2,
Signal::Note(midi) => 1,
}
}
fn main() -> Word {
loudness(Signal::Note(72))
}
keleusma runで実行します。出力は次のとおりです。
2
値はSignal::Note(72)です。最初のアームはSignal::Restを求めていて、当てはまりません。次のアームはSignal::Noteを求め、その持ち運んだ値をmidiとして束縛し、さらにwhen midi >= 60という条件で絞り込みます。アームに付いたwhenが「ガード」です。アームはパターンが当てはまり、かつガードがtrueのときだけ実行されます。ここでは72 >= 60がtrueなので、アームが実行されて結果は2になります。値はSignal::Note(72)です。最初のアームはSignal::Restを求めていて、当てはまりません。次のアームはSignal::Noteを求め、その持ち運んだ値をmidiとして束縛し、さらにwhen midi >= 60という条件で絞り込みます。アームに付いたwhenが「ガード」です。アームはパターンが当てはまり、かつガードがtrueのときだけ実行されます。ここでは72 >= 60がtrueなので、アームが実行されて結果は2になります。
もし音が60未満だったら、ガードがfalseになり、マッチは次のアームSignal::Note(midi)に進みます。これにはガードが無いので、結果は1になります。もし音が60未満だったら、ガードがfalseになり、マッチは次のアームSignal::Note(midi)に進みます。これにはガードが無いので、結果は1になります。
match は網羅されていなければならない
すべてのmatchはあらゆる可能な値に対応しなければなりません。列挙型に対するmatchは、すべてのヴァリアントがカバーされていれば網羅されていることになり、言語がそれを確認してくれます。Wordに対するmatchは、列挙するには値の数が多すぎるので、_のワイルドカードで網羅性を満たします。すべてのmatchはあらゆる可能な値に対応しなければなりません。列挙型に対するmatchは、すべてのヴァリアントがカバーされていれば網羅されていることになり、言語がそれを確認してくれます。Wordに対するmatchは、列挙するには値の数が多すぎるので、_のワイルドカードで網羅性を満たします。
網羅性は形式的なものではありません。それは、どんな値が来てもmatchが結果を生み出すという保証です。プログラムが忘れたケースは無い、ということです。網羅性は形式的なものではありません。それは、どんな値が来てもmatchが結果を生み出すという保証です。プログラムが忘れたケースは無い、ということです。
タプルや構造体を分解する
matchは列挙型に対して最も力を発揮します。そこでは言語が網羅性を正確に確認できます。この章で出てきた他の形については、もう少し単純な道具がすでに用意されています。タプルは第12章で示したようにlet (a, b) = ...で分解します。構造体のフィールドは第10章で示したようにvalue.fieldで読みます。まずこれらを使い、matchは列挙型のヴァリアントを選ぶときと、リテラルの値を選ぶときに使うのが良いです。matchは列挙型に対して最も力を発揮します。そこでは言語が網羅性を正確に確認できます。この章で出てきた他の形については、もう少し単純な道具がすでに用意されています。タプルは第12章で示したようにlet (a, b) = ...で分解します。構造体のフィールドは第10章で示したようにvalue.fieldで読みます。まずこれらを使い、matchは列挙型のヴァリアントを選ぶときと、リテラルの値を選ぶときに使うのが良いです。
この章のまとめ
matchのアームは、パターン、=>、結果、という形です。- パターンはリテラル、束縛、ワイルドカード
_、そして持ち運んだ値を束縛する列挙型ヴァリアントパターンです。パターンはリテラル、束縛、ワイルドカード_、そして持ち運んだ値を束縛する列挙型ヴァリアントパターンです。 whenガードはアームをさらに条件で絞り込みます。- すべての
matchは網羅されていなければならず、言語がそれを強制します。
次の章では、一つの関数を複数の頭部に広げます。