Flutter を触っていて、 Javascript と同じように List の reduce
を使おうとしたらハマったので、皆さんも気をつけましょう。
こちらの記事のコードは DartPad で試せます。
書いている時点のバージョンは Flutter 1.22.0-12.1.pre Dart SDK 2.9.3
です。
要約
- Dart の
reduce
は要素が一つの場合、そのまま値を返す。 - Dart の
reduce
は List の要素の型と結果の型が変わる場合は使えない。
Dart の reduce
ドキュメントはこちらになります。
早速実行してみましょう。
void main() {
List<int> test = [6, 9, 13];
print(test.reduce((acc, element) {
return acc + element;
}));
}
Console には 28
と表示されました。これぐらい単純ならちゃんと動きます。
ブラックジャックの例
ブラックジャックの得点計算を例にします。各カードの得点は以下のようになります。
- 2~10 : 数字の点数
- J,Q,K : 10点
(A の得点については本題と関係ないので省略)
では、 reduce
を使って点数を計算します。
void main() {
List<int> test = [6, 9, 13];
print(test.reduce((acc, element) {
if (element > 10) {
return acc + 10;
}
return acc + element;
}));
}
Console には 25
と表示されました。うまく動いていそうです。
ハマるケース
ではどんなケースでうまく動かないのか。それは K 一枚のケースです。
void main() {
List<int> test = [13];
print(test.reduce((acc, element) {
if (element > 10) {
return acc + 10;
}
return acc + element;
}));
}
Console には 13
と表示されました。List の要素が一つの時はそのまま値を返すようです。
正しいやり方
この問題に対処するには List.fold
を使います。ドキュメントはこちら
void main() {
List<int> test = [13];
print(test.fold<int>(0, (acc, element) {
if (element > 10) {
return acc + 10;
}
return acc + element;
}));
}
Console には 10
と表示されました。
結論
実際、最初のケースのように reduce
の内部で特別な処理をしなければ問題はないです。
しかし、メンテナンスを通して特別な処理が入ることも考えられるので、(個人的な意見ですが)一律で fold
を使うようした方がいいです。
おまけ
reduce
は List の要素の型と結果の型が変わる場合は型エラーになります。
以下の例では List の要素が int
で結果の型が double
になります。
void main() {
List<int> test = [6, 9, 13];
print(test.reduce((acc, element) {
return acc + (element / 2); // 型エラー!
}));
print(test.fold<double>(0, (acc, element) {
return acc + (element / 2); // エラーになりません
}));
}