Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

第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 >= 60trueなので、アームが実行されて結果は2になります。値はSignal::Note(72)です。最初のアームはSignal::Restを求めていて、当てはまりません。次のアームはSignal::Noteを求め、その持ち運んだ値をmidiとして束縛し、さらにwhen midi >= 60という条件で絞り込みます。アームに付いたwhenが「ガード」です。アームはパターンが当てはまり、かつガードがtrueのときだけ実行されます。ここでは72 >= 60trueなので、アームが実行されて結果は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は網羅されていなければならず、言語がそれを強制します。

次の章では、一つの関数を複数の頭部に広げます。