Dart / Pattern

DartのPatternsに関するキャッチアップ・メモ等

Petternsとは
- "文(Statement)" や "式(Expression)" と同様に、Dart言語における構文カテゴリ
- 実際の値とマッチする可能性のある値の集合の形を表す
- 文脈やパターンの形によって以下どちらか、または両方を行う
- 値にマッチする
- 値を分解する

値のマッチング
- 特定の値に対して、期待する形をしているかどうかを検証する
- 使用するPatternsの種類によってマッチ条件は異なる
// 例:定数パターンは、`1 == number` の場合にマッチする
switch (number) {
case 1:
print('one');
}

サブパターン
パターンはそのサブパターンに再帰的にマッチする(どういうこと?)
例えば、コレクション型パターンの個々のフィールドは、変数パターンや定数パターンである可能性がある。(どういうこと?)
const a = 'a';
const b = 'b';
switch (obj) {
// List pattern [a, b] matches obj first if obj is a list with two fields,
// then if its fields match the constant subpatterns 'a' and 'b'.
case [a, b]:
print('$a, $b');
}

値の分解
- オブジェクトとパターンがマッチすると、パターンはオブジェクトのデータにアクセスし、それを部分的に取り出すことができる(オブジェクトの分解)
var numList = [1, 2, 3];
// numListから3つの要素を分解し、a, b, cという名前の新しい変数として宣言する
var [a, b, c] = numList;
print(a + b + c);
分解するパターンの中には、どんな種類のパターンでも入れ子にすることができる。
例えば、このケースパターンは最初の要素が 'a' または 'b' である2要素リストにマッチし分解する。
switch (list) {
case ['a' || 'b', var c]:
print(c);
}

Patternsを使用できる場所
- ローカル変数の宣言と代入
- for / for-in
- if-case / swich-case
- コレクションリテラルの制御フロー

パターン変数の宣言
- ローカル変数の宣言が可能な場所であればどこでも使用可能
- パターンは右側の値とマッチする
- マッチした場合、その値を分解して新しいローカル変数に割り当てる
// 新しい変数a、b、cを宣言する
var (a, [b, c]) = ('str', [1, 2]);
パターン変数の宣言は、varかfinalで始まり、その後にパターンが続かなければならない。

パターン変数への代入
- 変数代入パターンは代入の左側に当たる。
- まず、マッチしたオブジェクトを分解し、次に新しい変数をバインドするのではなく、既存の変数に値を代入する
変数代入パターンを使って、3つ目の一時的な変数を宣言s売ることなく、2つの変数の値を入れ変える
var (a, b) = ('left', 'right');
(b, a) = (a, b); // Swap.
print('$a $b'); // Prints "right left".

パターン変数の宣言と代入は、ローカル変数に対してだけなのか?についての検証を行った
まず変数宣言についての検証。
適当にData
クラスを定義して、フィールドとしてパターン変数の宣言を記述してみた。
class Data {
final (a, b) = ('a', 'b');
}
A pattern variable declaration may not appear outside a function or method.Try declaring ordinary variables and assigning from within a function or method.
訳:パターン変数の宣言は、関数やメソッドの外には出てきません。普通の変数を宣言して、関数やメソッドの中から代入してみてください。
上記のコンパイルエラーが発生。宣言はローカル変数でないとNGのようだ
次に代入はどうだろう。
先ほどと同様にData
クラスを定義し、既存の変数a
,b
に対し、メソッドから代入を行うように記述してみた。
class Data {
var a;
var b;
void method() {
(a, b) = ('a', 'b');
}
}
Only local variables can be assigned in pattern assignments.Try assigning to a local variable.
訳: パターン・アサインで代入できるのはローカル変数だけです。ローカル変数に代入してみてください。
こちらも同様、コンパイルエラーが発生した。
以上の結果から、パターンを使用した変数の宣言・代入はいずれもローカル変数に対してのみ有効ということがわかった

Case節
- すべての case 節(switch文/式,if-case文)ではパターンが使用できる。
- case節でのパターンは反駁可能(Refutable)であるため、マッチしない場合でもエラーをスローしない
- パターンがcase節で分解した値は、そのcaseブロック内をスコープとするローカル変数となる。
void main() {
const first = 0;
const last = 10;
dynamic value = 5;
switch (value) {
case 1:
print('one');
case >= first && <= last:
print('in range');
case (var a, var b):
print('a = $a, b = $b');
default:
print('default');
}
}
if-caseの場合
void main() {
const first = 0;
const last = 10;
dynamic value = 5;
if (value case 1) {
print('one');
} else if (value case >= first && <= last) {
print('in range');
} else if (value case (var a, var b)) {
print('a = $a, b = $b');
} else {
print('default');
}
}

for / for-in ループ
Map<String, int> hist = {
'a': 23,
'b': 100,
};
for (var MapEntry(key: key, value: count) in hist.entries) {
print('$key occurred $count times');
}
object petternの変数名は省略可能
for (var MapEntry(:key, value: count) in hist.entries) {
print('$key occurred $count times');
}
List<(String, int)> hist = [
('a', 23),
('b', 100),
];
for (final (key, count) in hist) {
print('$key occurred $count times');
}