Skip to content

step5 match式を用いた条件分岐による判定と判別共用体

Step 4 では、if 式を使った条件分岐を学んだ。 しかし、if 式による条件分岐は選択肢が多くなると煩雑になりがちである。Step 5 では、より洗練された方法で条件分岐を行う match 式と、型の安全性を高めるための判別共用体について学ぶ。

match 式の基本構文

F# の match 式は、パターンマッチングを用いて値に応じて異なる処理を実行する強力な構文である。 基本的な構文は以下の通りとなる。

match 判定する値 with
| パターン1 -> パターン1にマッチしたときの処理
| パターン2 -> パターン2にマッチしたときの処理
| パターン3 -> パターン3にマッチしたときの処理
| _ -> どのパターンにもマッチしなかったときの処理

matchwith は予約語である。 | は各パターンを区切るために使用する。 -> はパターンと処理を紐づける。 _ はワイルドカードパターンと呼ばれ、どのパターンにもマッチしなかった場合にマッチする。

例えば、入力された値が answer より大きいか、小さいか、等しいかを判定する match 式は以下のようになる。

let answer = 42
let input = 24
let result =
match input with
| x when x > answer -> "大きすぎます!"
| x when x < answer -> "小さすぎます!"
| _ -> "正解!"
printfn "%s" result

この例では、input の値を評価し、それぞれのパターンと比較している。 when キーワードを使うと、パターンにガード節(追加の条件)を設定することができる。 x はパターン変数と呼ばれ、マッチした値が束縛される。 最後のパターンは _ なので、inputanswer と等しい場合にマッチする。

TypeScript の switch 文との比較

match 式は、TypeScript の switch 文と似ているが、より強力な機能を持っている。

TypeScript で同様の処理を switch 文で書くと、以下のようになるだろう。

const answer = 42;
const input = 24;
let result: string;
switch (true) {
case input > answer:
result = "大きすぎます!";
break;
case input < answer:
result = "小さすぎます!";
break;
default:
result = "正解!";
}
console.log(result);

TypeScript の switch 文では、case ラベルに値を指定し、break 文を使ってフォールスルーを防ぐ必要がある。 一方、F# の match 式では、パターンが上から順に評価され、最初にマッチしたパターンの処理が実行されるため、break 文は不要だ。 また、match 式は値を返す式であるため、result 変数に直接結果を代入することができる。

ts-pattern ライブラリとの比較

TypeScript で F# の match 式のようなパターンマッチングを実現するライブラリとして、ts-pattern がある。

ts-pattern を使うと、先ほどの例は以下のように書ける。

import { match, P } from "ts-pattern";
const answer = 42;
const input = 24;
const result = match(true)
.with(
P.when(() => input > answer),
() => "大きすぎます!"
)
.with(
P.when(() => input < answer),
() => "小さすぎます!"
)
.otherwise(() => "正解!");
console.log(result);

ts-pattern を使うと、F# の match 式に近い形でパターンマッチングを記述できる。 ただし、ts-pattern はあくまでライブラリであり、言語組み込みの機能ではないことに注意が必要だ。

判別共用体 (Discriminated Unions)

判別共用体は、いくつかの異なる種類の値を、型安全に表現するための機能である。 数当てゲームでは、「大きすぎ」、「小さすぎ」、「正解」の 3 つの状態を判別共用体で表現できる。

type GameResult =
| TooBig
| TooSmall
| Correct

このコードでは、GameResult という名前の新しい型を定義している。 GameResult 型は、TooBigTooSmallCorrect の 3 つの値のいずれかを取ることができる。 これらの値は判別子と呼ばれる。

判別共用体と match 式の組み合わせ

判別共用体と match 式を組み合わせることで、より安全で表現力豊かなコードを書くことができる。 先ほどの例を、判別共用体を使って書き直すと以下のようになる。

type GameResult =
| TooBig
| TooSmall
| Correct
let answer = 42
let input = 24
let result =
match compare input answer with
| 1 -> TooBig
| -1 -> TooSmall
| _ -> Correct
match result with
| TooBig -> printfn "大きすぎます!"
| TooSmall -> printfn "小さすぎます!"
| Correct -> printfn "正解!"

compare 関数は、2 つの値を比較し、最初の値が大きければ 1、小さければ -1、等しければ 0 を返す組み込み関数である。 この例では、まず compare 関数を使って inputanswer を比較し、その結果を match 式で評価して、対応する Result 型の値を result に代入している。 そして、もう一度 match 式を使って result の値に応じたメッセージを出力している。

Program.fs への適用

それでは、Program.fs を書き換えて、match 式と判別共用体を使った数当てゲームの判定ロジックを実装してみよう。

let answer = 42
printfn "正解は%dです" answer
printfn "1から100までの数字を入力してください。"
let inputNumber = stdin.ReadLine() |> int
type GameResult =
| TooBig
| TooSmall
| Correct
let result =
match compare inputNumber answer with
| 1 -> GameResult.TooBig
| -1 -> GameResult.TooSmall
| _ -> GameResult.Correct
let message =
match result with
| GameResult.TooBig -> "大きすぎます"
| GameResult.TooSmall -> "小さすぎます"
| GameResult.Correct -> "正解です"
printfn "%s" message

書き換えたら dotnet run で実行してみよう。 実行結果は if 式を使った場合と同じになる。

まとめ

このステップでは、以下の内容を学んだ。

  • match 式を使ったパターンマッチング
  • 判別共用体による型安全な値の表現
  • compare 関数を使った値の比較
  • when キーワードを使ったパターンのガード
  • F#の match 式と、TypeScript の switch 文や ts-pattern ライブラリとの比較
  • match 式の網羅性チェック
  • 配列のパターンマッチング (余談)

match 式と判別共用体は、F# の強力な機能であり、複雑な条件分岐を簡潔かつ安全に記述する上で非常に役立つ。 適切に活用することで、コードの可読性と保守性を向上させることができるだろう。

次のステップでは、関数の定義方法について学ぶ。