Dart 3.0で登場したパターンマッチングは、条件分岐を直感的かつ柔軟に記述するための強力な機能です。特にswitch文において、この機能を使うことで、型や値、複雑な条件に基づいた分岐を簡潔に記述できます。
変数でのパターンマッチ
変数でのパターンマッチを使用することで、引数の型に基づいて異なる処理を行うことが可能です。以下の例では、2つの引数の型に応じて異なる演算を行い、その結果を文字列で返しています。
String? pattern(a, b) {
switch ((a, b)) {
case (int a, int b):
return (a + b).toString();
case (int a, double b):
return (a * b).toString();
default:
return '$a $b';
}
}
expect(pattern(1, 2), '3');
expect(pattern(2, 1.5), '3.0');
expect(pattern(2.5, 1.5), '2.5 1.5');
このコードは、引数が両方とも整数の場合は和を、整数と浮動小数点数の場合は積を計算し、それ以外の組み合わせでは単に引数を文字列として返します。
クラスに基づくパターンマッチ
クラスのインスタンスに基づいて条件分岐を行うことも、パターンマッチングを使えば簡単に実現できます。以下の例では、Personクラスのインスタンスに基づいて、異なる文字列を返しています。expectを使用して、各ケースの期待される出力をテストしています。
String pattern(object) {
switch (object) {
case Person(company: String b) when b == 'Toyota':
return 'Person working for Toyota';
case Person(company: null):
return 'No worker';
default:
return 'Person not working for Toyota';
}
}
expect(pattern(Person(company: 'Toyota', name: 'Suzuki')), 'Person working for Toyota');
expect(pattern(Person(name: 'Suzuki')), 'No worker');
expect(pattern(Person(company: 'Honda', name: 'Suzuki')), 'Person not working for Toyota');
このコードは、Personオブジェクトのcompanyプロパティをチェックし、特定の条件に応じて異なるメッセージを返します。
値の比較でパターンマッチ
値の比較でパターンマッチを使うことで、値の範囲に基づいた条件分岐を簡潔に記述できます。以下の例では、スコアに応じて異なるランクを返しています。expectを使用して、各スコアに対する期待されるランクをテストしています。
String getRank(int score) => switch (score) {
< 50 => '落第',
>= 50 && < 60 => '一応合格',
>= 60 && < 90 => '合格',
>= 90 => '優秀に合格',
_ => throw Exception('到達不可能'),
};
expect(getRank(49), '落第');
expect(getRank(50), '一応合格');
expect(getRank(60), '合格');
expect(getRank(61), '合格');
expect(getRank(90), '優秀に合格');
このコードは、スコアの値に応じて、落第、一応合格、合格、優秀に合格のいずれかの文字列を返します。値は網羅しているつもりですが、コンパイルでは発見できないのでデフォルトをつけています。
続けて、Dartのパターンマッチングを活用したswitch文に関する記事に、「パターンマッチングのメソッドを作成」する方法を追加します。このセクションでは、具体的なメソッドの実装例とその解説を行います。
パターンマッチングのメソッドを作成
パターンマッチングを利用したメソッドを作成することで、コードの再利用性と可読性を高めることができます。以下に示す例では、異なるシナリオでパターンマッチングを活用する方法を紹介します。
文字列のパターンマッチング
String switchReturn(String value) {
return switch (value) { 'a' => 'aa', 'b' || 'c' => 'bb', _ => 'zz' };
}
String switchReturn2(String value) =>
switch (value) { 'a' => 'aa', 'b' || 'c' => 'bb', _ => 'zz' };
これらのメソッドは、入力された文字列に応じて異なる文字列を返します。'a'の場合は'aa'を、'b'または'c'の場合は'bb'を、それ以外の場合は'zz'を返します。ここでのポイントは、'b' || 'c'という表記を使って複数のケースを一つの結果にマッピングしていることです。
クラスインスタンスのパターンマッチング
bool isToyotaWorker1(object) {
switch (object) {
case Person(company: String b) when b == 'Toyota':
return true;
default:
return false;
}
}
bool isToyotaWorker2(value) => switch (value) {
Person(company: String b) when b == 'Toyota' => true,
_ => false
};
bool isToyotaWorker3(person) {
if (person case Person(company: String b) when b == 'Toyota') {
return true;
} else {
return false;
}
}
これらのメソッドは、Personクラスのインスタンスが表す人物がトヨタの従業員かどうかを判定します。isToyotaWorker1とisToyotaWorker2はswitch文を使用し、isToyotaWorker3はif文にパターンマッチングを組み合わせています。これらの例では、whenキーワードを使用して追加の条件(この場合はcompanyプロパティが'Toyota'であること)を指定しています。
テストケース
expect(isToyotaWorker1(Person(company: 'Toyota', name: 'Suzuki')), true);
expect(isToyotaWorker2(Person(company: 'Toyota', name: 'Suzuki')), true);
expect(isToyotaWorker3(Person(company: 'Toyota', name: 'Suzuki')), true);
expect(isToyotaWorker1(Person(company: 'Honda', name: 'Suzuki')), false);
expect(isToyotaWorker2(Person(company: 'Honda', name: 'Suzuki')), false);
expect(isToyotaWorker3(Person(company: 'Honda', name: 'Suzuki')), false);
expect(isToyotaWorker1(Person(name: 'Suzuki')), false);
expect(isToyotaWorker2(Person(name: 'Suzuki')), false);
expect(isToyotaWorker3(Person(name: 'Suzuki')), false);
これらのテストケースは、上記で定義したメソッドが期待通りに動作することを検証します。トヨタの従業員を表すPersonインスタンスに対してはtrueを、それ以外の場合はfalseを返すことを確認しています。
まとめ
Dartにおけるパターンマッチングを活用したswitch文は、コードの可読性と表現力を大幅に向上させます。変数パターン、クラスに基づくパターン、関係パターンを駆使することで、複雑な条件分岐も簡潔かつ明確に記述することが可能です。expectを使用したテストケースを含めることで、実際の動作を確認しながら、より確実にコードを実装することができます。この機能を活用することで、Dartでのプログラミングがより快適になるでしょう。