パターンマッチ1
F#でプログラムを行う上で重要な機能の一つに
パターンマッチがあります。
C言語をご存じの方は、
賢くなったswitch文だと思って貰えれば
イメージ出来るかと思います。
パターンマッチを使えば、if文と同じく場合分けを記述することが出来ます。
正確な構文を書くとかなり複雑になるため
簡略化した構文は以下のようになります。
個別のパターンについては次の節で説明します。
- パターンマッチの構文
- match パターン1 with
| パターン2 -> 式
| パターン3 -> 式
...
| パターンn -> 式
パターン2,3,..,nとパターン1は同じ型である必要があります。
パターンは上から順にマッチを行い
一致した箇所の式が実行されます。
パターンに一致したら、C言語のswitch文でいうとbreakが挿入されているように
以後のパターンは実行されません
まずは、例を見てみましょう
パターンマッチの例
let kyoro kutibasi =
match kutibasi with
| "金のエンゼル" -> printfn "大当たり"
| "銀のエンゼル" -> printfn "あたり"
| _ -> printfn "はずれ"
in
kyoro "金のエンゼル";
kyoro "銀のエンゼル";
kyoro "普通のクチバシ";;
パターンマッチの例(軽量構文)
let kyoro kutibasi =
match kutibasi with
| "金のエンゼル" -> printfn "大当たり"
| "銀のエンゼル" -> printfn "あたり"
| _ -> printfn "はずれ"
kyoro "金のエンゼル"
kyoro "銀のエンゼル"
kyoro "普通のクチバシ"
これは、チョコボールの当たり判定を行う関数kyoroです。
kyoroは1つの引数kutibasiを持つ関数を定義しており、
match kutibasi withの部分で、
kutibasiを対象にパターンマッチを行うという意味になります。
そして、続く部分で、
"金のエンゼル"なら大当たりと表示
"銀のエンゼル"ならあたりと表示
それ以外ならはずれと表示
という内容を定義しています。
3番目のパターンとして'_'というパターンが使われていますが
これは、どんな場合にもマッチするパターンで
ワイルドカードパターンといいます。
C言語のswitch文でいえば、defaultケースに相当するものです。
もし、_の代わりに単にxといった変数をおいても
実は全く同じ結果になりますが
xを使った場合はx -> xとするとマッチしたものが表示されるのに対し
ワイルドカードパターンの場合は
_ -> _のようにすることは出来ず、エラーになります。
つまり、ワイルドカードパターンを使用した箇所では
マッチしたパターンを使わないという意思表示にもなります。
実は、同じ内容はif文を使ってプログラムすることが出来ます。
if文を用いた場合
let kyoro kutibasi =
if kutibasi = "金のエンゼル" then printfn "大当たり"
elif kutibasi = "銀のエンゼル" then printfn "あたり"
else printfn "はずれ"
in
kyoro "金のエンゼル";
kyoro "銀のエンゼル";
kyoro "普通のクチバシ";;
if文を用いた場合(軽量構文)
let kyoro kutibasi =
if kutibasi = "金のエンゼル" then printfn "大当たり"
elif kutibasi = "銀のエンゼル" then printfn "あたり"
else printfn "はずれ"
kyoro "金のエンゼル"
kyoro "銀のエンゼル"
kyoro "普通のクチバシ"
では、なぜパターンマッチを用いるのかといえば
次のような理由が考えられます。
・ifに比べて、場合分けがすっきり記述出来る
・パターンの過不足を検出出来る
例として、マッチしないパターンを教えてくれる例を挙げてみます。
パターンマッチは上から順に行われるという性質があるため
先ほどの紹介した'_'を最初のパターンに書いた場合
以降のパターンには全てマッチしないという事態になります。
当たらないチョコボール
let kyoro kutibasi =
match kutibasi with
| _ -> printfn "はずれ"
| "金のエンゼル" -> printfn "大当たり"
| "銀のエンゼル" -> printfn "あたり"
in
kyoro "金のエンゼル";
kyoro "銀のエンゼル";
kyoro "普通のクチバシ";;
当たらないチョコボール(軽量構文)
let kyoro kutibasi =
match kutibasi with
| _ -> printfn "はずれ"
| "金のエンゼル" -> printfn "大当たり"
| "銀のエンゼル" -> printfn "あたり"
kyoro "金のエンゼル"
kyoro "銀のエンゼル"
kyoro "普通のクチバシ"
このプログラムをコンパイルすると、
コンパイラは次のような警告を出して
マッチしないパターンがあるということを教えてくれます。
コンパイラの警告
warning FS0026: This rule will never be matched.
whenキーワード
おもちゃのカンヅメを手に入れるためには
通常、銀のエンゼルは5枚ほど必要です。
そこで、クチバシの種類と枚数を引数にする次のような関数を考えてみます
改良版kyoro
let kyoro kutibasi maisuu =
if kutibasi = "金のエンゼル" then printfn "大当たり"
elif kutibasi = "銀のエンゼル" && maisuu >= 5 then printfn "大当たり"
else printfn "はずれ";;
kyoro "銀のエンゼル" 4;; //はずれ
kyoro "銀のエンゼル" 5;; //大当たり
改良版kyoro(軽量構文)
let kyoro kutibasi maisuu =
if kutibasi = "金のエンゼル" then printfn "大当たり"
elif kutibasi = "銀のエンゼル" && maisuu >= 5 then printfn "大当たり"
else printfn "はずれ"
kyoro "銀のエンゼル" 4 //はずれ
kyoro "銀のエンゼル" 5 //大当たり
もちろんこれでうまく動いているのですが
これをパターンマッチで書くにはどうしたら良いでしょう?
実は、ここまで説明してきた事項だけでは
このパターンをコンパクトに書くことが出来ません。
なぜならば、maisuu >= 5という条件を
一つのパターンで表すことは出来ないからです。
こういう特定のパターンに条件を付けたパターンを記述したい場合に
利用出来るのがwhenキーワードです。
最初に出てきたパターンの構文をもう少し詳しく書くと
次のようになります
- whenキーワード
- match パターン1 with
| パターン2 [when 式] -> 式
| パターン3 [when 式] -> 式
...
| パターンn [when 式] -> 式
このwhenキーワードは、後ほど紹介する
どんなパターンともくっつけることができます。
whenキーワードを用いることで
if文と同じ内容のプログラムは
すべてパターンマッチを使っても記述出来るようになります。
whenキーワードを用いたkyoro
let kyoro kutibasi maisuu =
match (kutibasi,maisuu) with
| ("金のエンゼル",_) -> printfn "大当たり"
| ("銀のエンゼル",x) when x>=5 -> printfn "大当たり"
| _ -> printfn "はずれ";;
このプログラムでは
2行目の("銀のエンゼル",x) when x>=5とある箇所が
・kutibasiの種類が"銀のエンゼル"で
・枚数が5枚以上
ということを表しています。
そのため、銀のエンゼルかつ、4枚以下の場合は
最後の_のパターンにマッチします。
なお、ここで使っている(kutibasi,maisuu)というのは
タプルパターンというパターンで
これについては次の節以降で説明していきます。
functionキーワード
関数の最後の引数に対してパターンマッチする場合
functionキーワードを用いた
省略形を利用することが出来ます。
- functionキーワード
- 式 := function ルール
ルール :=
| パターン1 [when 式] -> 式
| パターン2 [when 式] -> 式
...
| パターンn [when 式] -> 式
最初のkyoro関数は1引数の関数であるので
functionキーワードを用いて書き直すことができます。
具体的には、次の2パターンは同じ意味になります
上で学んだ書き方
let kyoro kutibasi =
match kutibasi with
| "金のエンゼル" -> printfn "大当たり"
| "銀のエンゼル" -> printfn "あたり"
| _ -> printfn "はずれ"
functionキーワードを使った書き方
let kyoro = function
| "金のエンゼル" -> printfn "大当たり"
| "銀のエンゼル" -> printfn "あたり"
| _ -> printfn "はずれ"
つまり、functionキーワードを使った場合
元のkyoro関数の最後の引数であったkutibasiが消えて
暗黙的にmatch kutibasi withと書かれているような動作になります。