はじめに
前回(第1回)では Dart の変数・型・演算子を学びました。今回は 制御構文 を扱います。
プログラムの実行の流れを制御する「条件分岐」と「繰り返し」は、あらゆるアプリケーション開発で必須の知識です。
この記事で学ぶこと
-
if/else if/else 三項演算子-
forループ(基本、for-in) -
while/do-while -
break/continue -
switch/case(Dart 3.0 の switch expression を含む) assert
コード例はすべて DartPad で実行できます。
1. if / else if / else
基本構文
void main() {
int score = 85;
if (score >= 90) {
print('評価: A');
} else if (score >= 80) {
print('評価: B');
} else if (score >= 70) {
print('評価: C');
} else if (score >= 60) {
print('評価: D');
} else {
print('評価: F');
}
// 評価: B
}
条件式には bool 型のみ使用可能
Dart は JavaScript とは異なり、if の条件には厳密に bool 型の値が必要です。
void main() {
String name = 'Dart';
// OK: bool を返す式
if (name.isNotEmpty) {
print('名前が入力されています: $name');
}
// 名前が入力されています: Dart
// NG: 以下はコンパイルエラー
// if (name) { } // String は bool ではない
// if (1) { } // int は bool ではない
}
複合条件
void main() {
int age = 20;
bool hasTicket = true;
if (age >= 18 && hasTicket) {
print('入場できます');
} else if (age >= 18 && !hasTicket) {
print('チケットを購入してください');
} else {
print('18歳未満は入場できません');
}
// 入場できます
}
実践例: 成績判定
void main() {
int japanese = 78;
int math = 92;
int english = 65;
int total = japanese + math + english; // 235
double average = total / 3; // 78.33333333333333
print('国語: $japanese 点');
print('数学: $math 点');
print('英語: $english 点');
print('合計: $total 点');
print('平均: ${average.toStringAsFixed(1)} 点'); // 78.3
if (average >= 90) {
print('結果: 優秀');
} else if (average >= 70) {
print('結果: 良好');
} else if (average >= 50) {
print('結果: 要努力');
} else {
print('結果: 再試験');
}
// 結果: 良好
}
2. 三項演算子
条件 ? 真の値 : 偽の値 の形式で、if-else を1行で書けます。
void main() {
int age = 17;
String status = age >= 18 ? '成人' : '未成年';
print('$age 歳は$status'); // 17 歳は未成年
// 実用例: 値の範囲チェック
int score = 105;
int validScore = (score > 100) ? 100 : score;
print('スコア: $validScore'); // スコア: 100
// ネストも可能(ただし読みにくいので非推奨)
int value = 75;
String grade = value >= 90
? 'A'
: value >= 80
? 'B'
: value >= 70
? 'C'
: 'D';
print('グレード: $grade'); // グレード: C
}
3. for ループ
基本の for ループ
void main() {
// 1 から 5 までを出力
for (int i = 1; i <= 5; i++) {
print('i = $i');
}
// i = 1
// i = 2
// i = 3
// i = 4
// i = 5
}
カウントダウン
void main() {
for (int i = 5; i >= 1; i--) {
print('$i...');
}
print('発射!');
// 5...
// 4...
// 3...
// 2...
// 1...
// 発射!
}
for-in ループ(コレクションの走査)
リストなどのコレクションを走査するには for-in を使います。
void main() {
List<String> fruits = ['りんご', 'バナナ', 'みかん', 'ぶどう'];
for (String fruit in fruits) {
print('果物: $fruit');
}
// 果物: りんご
// 果物: バナナ
// 果物: みかん
// 果物: ぶどう
}
インデックスとともに走査
void main() {
List<String> languages = ['Dart', 'Python', 'Java', 'Go'];
for (int i = 0; i < languages.length; i++) {
print('${i + 1}番目: ${languages[i]}');
}
// 1番目: Dart
// 2番目: Python
// 3番目: Java
// 4番目: Go
}
ネストした for ループ: 九九の表
void main() {
for (int i = 1; i <= 9; i++) {
String row = '';
for (int j = 1; j <= 9; j++) {
// padLeft で桁を揃える
row += '${(i * j).toString().padLeft(3)}';
}
print(row);
}
// 1 2 3 4 5 6 7 8 9
// 2 4 6 8 10 12 14 16 18
// 3 6 9 12 15 18 21 24 27
// 4 8 12 16 20 24 28 32 36
// 5 10 15 20 25 30 35 40 45
// 6 12 18 24 30 36 42 48 54
// 7 14 21 28 35 42 49 56 63
// 8 16 24 32 40 48 56 64 72
// 9 18 27 36 45 54 63 72 81
}
4. while / do-while
while ループ
条件が true の間、繰り返し実行します。条件を 先に 評価するため、条件が最初から false なら一度も実行されません。
void main() {
int count = 1;
while (count <= 5) {
print('count = $count');
count++;
}
// count = 1
// count = 2
// count = 3
// count = 4
// count = 5
print('ループ終了。最終 count = $count'); // ループ終了。最終 count = 6
}
do-while ループ
条件を 後に 評価するため、必ず1回は実行されます。
void main() {
int count = 10;
// while: 条件が最初から false なので実行されない
while (count < 5) {
print('while: $count');
count++;
}
// (何も出力されない)
// do-while: 必ず1回は実行される
count = 10;
do {
print('do-while: $count'); // do-while: 10
count++;
} while (count < 5);
}
実践例: 合計が 100 を超えるまで加算
void main() {
int sum = 0;
int n = 1;
while (sum <= 100) {
sum += n;
n++;
}
// ループを抜けた時点で n は最後に加算した値の次の値
print('合計が100を超えたのは ${n - 1} まで足したとき'); // 合計が100を超えたのは 14 まで足したとき
print('合計: $sum'); // 合計: 105
// 検算: 1+2+3+...+14 = 14*15/2 = 105
}
5. break / continue
break ― ループを抜ける
void main() {
for (int i = 1; i <= 10; i++) {
if (i == 6) {
print('$i で中断します');
break;
}
print('i = $i');
}
// i = 1
// i = 2
// i = 3
// i = 4
// i = 5
// 6 で中断します
}
continue ― 次の反復へスキップ
void main() {
for (int i = 1; i <= 10; i++) {
if (i % 3 == 0) {
continue; // 3 の倍数はスキップ
}
print('i = $i');
}
// i = 1
// i = 2
// i = 4
// i = 5
// i = 7
// i = 8
// i = 10
}
実践例: リストから特定の要素を探す
void main() {
List<int> numbers = [4, 7, 2, 9, 1, 8, 3, 6, 5];
int target = 8;
for (int i = 0; i < numbers.length; i++) {
if (numbers[i] == target) {
print('$target はインデックス $i にあります');
break;
}
}
// 8 はインデックス 5 にあります
}
6. switch / case
基本の switch 文
void main() {
String dayOfWeek = 'Wednesday';
switch (dayOfWeek) {
case 'Monday':
print('月曜日');
break;
case 'Tuesday':
print('火曜日');
break;
case 'Wednesday':
print('水曜日');
break;
case 'Thursday':
print('木曜日');
break;
case 'Friday':
print('金曜日');
break;
case 'Saturday':
print('土曜日');
break;
case 'Sunday':
print('日曜日');
break;
default:
print('不正な値です');
}
// 水曜日
}
複数ケースをまとめる
Dart では複数の case をまとめるために、空の case を並べて最後にコードを記述します。
void main() {
String day = 'Saturday';
switch (day) {
case 'Monday':
case 'Tuesday':
case 'Wednesday':
case 'Thursday':
case 'Friday':
print('$day は平日です');
break;
case 'Saturday':
case 'Sunday':
print('$day は休日です');
break;
default:
print('不正な値です');
}
// Saturday は休日です
}
Dart 3.0 の switch expression
Dart 3.0 以降では、switch を 式 として使えます。値を返す用途に最適です。
void main() {
int httpStatusCode = 404;
String message = switch (httpStatusCode) {
200 => 'OK',
301 => 'Moved Permanently',
400 => 'Bad Request',
403 => 'Forbidden',
404 => 'Not Found',
500 => 'Internal Server Error',
_ => 'Unknown Status',
};
print('$httpStatusCode: $message'); // 404: Not Found
}
switch expression でパターンマッチング(Dart 3.0)
void main() {
int score = 82;
String grade = switch (score) {
>= 90 => 'A',
>= 80 => 'B',
>= 70 => 'C',
>= 60 => 'D',
_ => 'F',
};
print('スコア $score → 評価: $grade'); // スコア 82 → 評価: B
}
注意: switch expression(
=>を使う形式)と範囲パターン(>= 90など)は Dart 3.0 以降の機能です。Flutter 3.10 以降で使用できます。
7. assert(デバッグ用)
assert は開発中(デバッグモード)にのみ有効な検証文です。条件が false のとき AssertionError が発生します。リリースビルドでは無視されます。
void main() {
int age = 25;
// デバッグモードでのみ実行される
assert(age >= 0, '年齢は0以上でなければなりません');
// age が負の値だった場合、デバッグモードでは AssertionError が発生する
// int invalidAge = -1;
// assert(invalidAge >= 0, '年齢は0以上でなければなりません');
print('年齢: $age'); // 年齢: 25
}
DartPad での実行: DartPad はデバッグモードで動作するため
assertが有効です。dart runコマンドでもデバッグモードがデフォルトです。dart run --enable-assertsで明示的に有効にすることもできます。
8. 実践的なコード例
FizzBuzz
プログラミングの定番問題です。1 から 30 まで順番に出力し、3 の倍数なら Fizz、5 の倍数なら Buzz、両方の倍数なら FizzBuzz を出力します。
void main() {
for (int i = 1; i <= 30; i++) {
if (i % 15 == 0) {
print('FizzBuzz');
} else if (i % 3 == 0) {
print('Fizz');
} else if (i % 5 == 0) {
print('Buzz');
} else {
print(i);
}
}
// 1
// 2
// Fizz
// 4
// Buzz
// Fizz
// 7
// 8
// Fizz
// Buzz
// 11
// Fizz
// 13
// 14
// FizzBuzz
// 16
// ... (以降同様)
}
リストの集計
void main() {
List<int> scores = [85, 92, 78, 96, 64, 88, 73, 91];
int sum = 0;
int max = scores[0];
int min = scores[0];
int passCount = 0; // 80点以上を合格とする
for (int score in scores) {
sum += score;
if (score > max) {
max = score;
}
if (score < min) {
min = score;
}
if (score >= 80) {
passCount++;
}
}
double average = sum / scores.length;
print('受験者数: ${scores.length}'); // 受験者数: 8
print('合計点: $sum'); // 合計点: 667
print('平均点: ${average.toStringAsFixed(1)}'); // 平均点: 83.4
print('最高点: $max'); // 最高点: 96
print('最低点: $min'); // 最低点: 64
print('合格者数: $passCount'); // 合格者数: 5
// 検算: 85+92+78+96+64+88+73+91 = 667
// 平均: 667/8 = 83.375 → 83.4
// 80点以上: 85, 92, 96, 88, 91 → 5人
}
素数判定
void main() {
for (int n = 2; n <= 30; n++) {
bool isPrime = true;
for (int i = 2; i * i <= n; i++) {
if (n % i == 0) {
isPrime = false;
break;
}
}
if (isPrime) {
print('$n は素数');
}
}
// 2 は素数
// 3 は素数
// 5 は素数
// 7 は素数
// 11 は素数
// 13 は素数
// 17 は素数
// 19 は素数
// 23 は素数
// 29 は素数
}
練習問題
練習問題 1: うるう年判定
西暦 year がうるう年かどうかを判定するプログラムを作成してください。
うるう年の条件:
- 4 で割り切れる年はうるう年
- ただし、100 で割り切れる年はうるう年ではない
- ただし、400 で割り切れる年はうるう年
以下の年を判定してください: 2024, 1900, 2000, 2025
期待する出力:
2024年はうるう年です
1900年はうるう年ではありません
2000年はうるう年です
2025年はうるう年ではありません
模範解答
void main() {
List<int> years = [2024, 1900, 2000, 2025];
for (int year in years) {
bool isLeapYear;
if (year % 400 == 0) {
isLeapYear = true;
} else if (year % 100 == 0) {
isLeapYear = false;
} else if (year % 4 == 0) {
isLeapYear = true;
} else {
isLeapYear = false;
}
if (isLeapYear) {
print('$year年はうるう年です');
} else {
print('$year年はうるう年ではありません');
}
}
}
ポイント: 条件の優先順位に注意してください。400 で割り切れるかを最初にチェックし、次に 100、最後に 4 の順で判定します。順番を間違えると正しく判定できません。
- 2024: 400で割り切れない → 100で割り切れない → 4で割り切れる → うるう年
- 1900: 400で割り切れない → 100で割り切れる → うるう年ではない
- 2000: 400で割り切れる → うるう年
- 2025: 400で割り切れない → 100で割り切れない → 4で割り切れない → うるう年ではない
練習問題 2: 三角形の描画
* を使って、高さ n = 5 の三角形を描画してください。
期待する出力:
*
**
***
****
*****
さらに余力があれば、逆三角形も描画してみてください。
逆三角形の期待する出力:
*****
****
***
**
*
模範解答
void main() {
int n = 5;
// 三角形
print('--- 三角形 ---');
for (int i = 1; i <= n; i++) {
String row = '';
for (int j = 0; j < i; j++) {
row += '*';
}
print(row);
}
// 逆三角形
print('\n--- 逆三角形 ---');
for (int i = n; i >= 1; i--) {
String row = '';
for (int j = 0; j < i; j++) {
row += '*';
}
print(row);
}
}
ポイント: 外側の for が行を、内側の for が各行の * の個数を制御します。逆三角形では外側のループを n から 1 に向かってデクリメントします。Dart では '*' * i のように文字列の乗算も使えるため、内側のループの代わりに print('*' * i); と書くこともできます。
練習問題 3: switch expression で月の日数を求める
月番号(1〜12)を受け取り、Dart 3.0 の switch expression を使ってその月の日数を返すプログラムを作成してください。2月は28日(うるう年は考慮不要)とします。
以下の月を判定してください: 1, 2, 4, 7, 12
期待する出力:
1月は31日あります
2月は28日あります
4月は30日あります
7月は31日あります
12月は31日あります
模範解答
void main() {
List<int> months = [1, 2, 4, 7, 12];
for (int month in months) {
int days = switch (month) {
2 => 28,
4 || 6 || 9 || 11 => 30,
_ => 31,
};
print('$month月は${days}日あります');
}
}
ポイント: Dart 3.0 の switch expression では || で複数のパターンをまとめられます。_ はワイルドカードで、どのパターンにもマッチしなかった場合のデフォルト値を指定します。1月, 3月, 5月, 7月, 8月, 10月, 12月は31日なので、30日の月と28日の2月だけを列挙し、残りを _ で31日としています。
まとめ
| 構文 | 用途 | 特徴 |
|---|---|---|
if / else if / else
|
条件分岐 | 条件は bool 型のみ |
三項演算子 ? :
|
簡潔な条件分岐 | 値を返す式として使える |
for |
回数指定の繰り返し | for (初期化; 条件; 更新) |
for-in |
コレクションの走査 | for (要素 in コレクション) |
while |
条件が真の間繰り返し | 条件を先に評価 |
do-while |
条件が真の間繰り返し | 必ず1回は実行 |
break |
ループ脱出 | 最も内側のループから脱出 |
continue |
次の反復へスキップ | 残りの処理をスキップ |
switch / case
|
値による分岐 |
break が必要 |
| switch expression | 値を返す switch | Dart 3.0 以降。=> 構文 |
assert |
デバッグ検証 | リリースビルドでは無視 |
次回(第3回)は 関数の定義と使い方 を学びます。
参考
- Dart Language Tour - Control flow statements
- Dart Language Tour - Branches
- Dart Language Tour - Patterns
- Dart 3.0 Release Notes
- DartPad
@kotaro_ai_lab
AI活用や開発効率化について発信しています。フォローお気軽にどうぞ!