Flutterアプリケーション開発概論

条件分岐(if / switch、pattern)

この節の目的は、「場合によって処理を変える」 というプログラムの基本感覚を身につけることです。第2章では、2-2 で変数・型・null safety・List / Map を学びました。今回はその続きとして、入ってきた値を見て、どの処理を動かすかを決める方法を学びます。Dart では昔ながらの ifswitch に加えて、Dart 3 から patterns パターン が本格的に入り、switchif case で「値の形そのもの」を見て分岐できるようになりました。Patterns は Dart 3.0 以上で使え、Dart 3 は 2023年5月10日に公開されました。

1. 条件分岐とは何か

条件分岐とは、条件が成り立つかどうかで処理を変えることです。

たとえば、テストの点数が 60 点以上なら「合格」を表示し、それ未満なら「再提出」を表示する、という動きです。人は日常で自然にこれをやっています。雨なら傘を持つ。電車が遅れていたら別ルートを考える。プログラムでは、その判断を明示的に書きます。

歴史的には、if ... then ... else ... という考え方は 1960 年の ALGOL 60 の報告書にすでに見られます。ALGOL 60 は国際的なアルゴリズム記述言語として設計され、条件式や条件文を体系的に扱いました。つまり、いま私たちが書く if は、かなり古くからある「プログラムの基本構造」です。

2. if 文の基本

最初に学ぶのは if です。

if は、「もし条件が真ならこの処理をする」という最も基本的な分岐です。

void main() {
  int score = 78;

  if (score >= 60) {
    print('合格です');
  }
}

このコードでは、score >= 60 が真なら 合格です を表示します。>= は「以上」という意味です。7860 以上なので、表示されます。

ここで大事なのは、if の丸かっこの中には true か false になる式 を書くことです。

Dart は型安全な言語なので、他の一部言語のように数字をそのまま条件として扱いません。条件には bool 型、つまり真偽値になる式が必要です。Dart 公式も、分岐は boolean conditions を使う形で説明しています。

3. if / else で二択を作る

「条件が真のとき」だけではなく、「そうでないとき」も書きたい場合は else を使います。

void main() {
  int score = 45;

  if (score >= 60) {
    print('合格です');
  } else {
    print('再提出です');
  }
}

このコードでは、score が 60 未満なので 再提出です が表示されます。

初心者が最初につまずきやすいのは、「どちらか片方しか実行されない」ことです。

ifelse は競争ではなく、一つの条件に対して二つの道を用意する イメージです。

学校の例で考えると分かりやすいです。

  • 宿題が終わっていたらゲームする
  • 終わっていなければ宿題を続ける

この「もし〜なら、そうでなければ〜」が、そのまま if / else です。

4. else if で三択以上を作る

現実の判断は二択だけではないことが多いです。

たとえば、点数によって「優秀」「合格」「再提出」を分けたいことがあります。

そういうときは else if を使います。

void main() {
  int score = 88;

  if (score >= 90) {
    print('とても良いです');
  } else if (score >= 60) {
    print('合格です');
  } else {
    print('再提出です');
  }
}

この場合、score は 88 なので、最初の score >= 90 は false です。 次の score >= 60 は true なので、合格です が表示されます。

ここで覚えておきたいのは、上から順に判定され、最初に当てはまったところで止まる ということです。

だから順番はとても大切です。

次のように順番を間違えると、意図した結果になりません。

void main() {
  int score = 95;

  if (score >= 60) {
    print('合格です');
  } else if (score >= 90) {
    print('とても良いです');
  } else {
    print('再提出です');
  }
}

このコードでは 95 点でも最初の score >= 60 に当たるので、合格です が表示されます。 つまり、広い条件を先に書くと、細かい条件が届かなくなる のです。

5. switch 文とは何か

switch は、一つの値を複数の候補と比べて分岐する ときに便利です。

たとえば、曜日によって表示を変えるとき、メニュー番号ごとに動作を変えるとき、状態コードごとに処理を変えるときに向いています。

歴史をたどると、分岐を多枝に広げる考え方は古く、ALGOL 68 の改訂報告では、ALGOL 60 の conditional clause を一般化して case-clause にしたと説明されています。さらに、C 言語の系譜では、Dennis Ritchie の C Reference Manualswitch statement がはっきり記述されており、C 系言語で広く知られる形になっていきました。

Dart 公式では、switch は「値を一連の cases に対して評価する構文」で、各 case 句は pattern であると説明されています。つまり、今の Dart の switch は、昔ながらの「値の比較」だけでなく、「値の形の照合」までできる構文です。

6. switch 文の基本

まずは、一番シンプルな switch から見ます。

void main() {
  String command = 'OPEN';

  switch (command) {
    case 'OPEN':
      print('開く処理をします');
    case 'CLOSE':
      print('閉じる処理をします');
    default:
      print('不明なコマンドです');
  }
}

このコードでは command'OPEN' なので、開く処理をします が表示されます。

Dart の modern switch で大事なのは、空でない case は終わったら switch の外へ抜ける ことです。

C 系言語でよく出てくる break を毎回書かなくてもよい、という点は初心者にとってかなり助かります。Dart 公式も、non-empty case clauses do not require a break statement と説明しています。

7. switch が向いている場面

if / else if でも同じことはできます。 では、なぜ switch があるのでしょうか。 答えは、一つの値を、複数の決まった候補と比べるときに読みやすいからです。

たとえば、曜日番号を文字に変える例を見てください。

void main() {
  int day = 3;

  switch (day) {
    case 1:
      print('月曜日');
    case 2:
      print('火曜日');
    case 3:
      print('水曜日');
    case 4:
      print('木曜日');
    case 5:
      print('金曜日');
    default:
      print('土日または不正な値');
  }
}

これは if / else if でも書けますが、switch のほうが「候補の一覧」を見やすいです。 つまり、if は「条件そのもの」を書くのが得意で、switch は「候補を並べる」のが得意です。

8. pattern とは何か

ここで Dart らしい新しい話が出てきます。

それが pattern パターン です。

Dart 公式では、pattern は「値が持つ形 shape を表し、それに実際の値が一致するかを調べるもの」と説明されています。さらに pattern は、値を match させるだけでなく、destructure 分解して中身を取り出す こともできます。Patterns は Dart 3.0 以上で使えます。

もう少しかみくだくと、pattern は

「この値は、こういう形をしているはずだ」

という期待を書ける仕組みです。

歴史的には、pattern matching の考え方は関数型言語の流れで強まりました。

2020 年の The History of Standard ML は、Standard ML が datatypes with associated case analysis by pattern matching を popularize したと説明しています。つまり、パターンマッチは関数型言語で大きく育ち、その考え方が Dart にも入ってきたと見てよいです。

9. Dart の pattern をやさしく体験する

9-1. if case

Dart では if case が使えます。

これは「if しながら pattern で照合する」構文です。

void main() {
  var pair = [10, 20];

  if (pair case [int x, int y]) {
    print('x = $x, y = $y');
  } else {
    print('2つの整数ではありません');
  }
}

このコードでは、pair が「整数が2個入った List の形」なら、xy に取り出します。 Dart 公式にも if (pair case [int x, int y]) の例があります。

ここでの面白い点は、一致するかどうかを見ながら、同時に中身を取り出せる ことです。

これが pattern の強さです。

9-2. switch と pattern

switch でも pattern が使えます。

void main() {
  var value = [1, 2];

  switch (value) {
    case [int a, int b]:
      print('2つの整数です: $a, $b');
    default:
      print('別の形です');
  }
}

このコードは、value が「整数2個の List」なら、その中身を ab に分けて表示します。

昔ながらの switch は「1 ならこれ、2 ならこれ」という比較が中心でした。

今の Dart の switch は、値の中身の形まで見られる のが大きな進化です。

この考え方は、後で records やより複雑なデータ構造を扱うときに強く効いてきます。

10. if と switch と pattern の使い分け

ここまで来ると、三つの違いを整理したくなります。

if が向いているとき

  • 条件が比較や論理式で表せるとき
  • score >= 60 のように、範囲や複雑な条件を書くとき
  • 「A かつ B」のような条件を作るとき

switch が向いているとき

  • 一つの値を、複数候補と比べるとき
  • メニュー番号、状態コード、曜日番号など
  • 候補の一覧を見やすく書きたいとき

pattern が向いているとき

  • 値の「形」を見たいとき
  • List や records の中身を分解したいとき
  • 一致確認と取り出しを同時にしたいとき

迷ったら

  • 単純な条件なら if
  • 候補一覧なら switch
  • 値の形まで見たいなら pattern

11. 飽きずに分かるミニ実践

実践1:テスト判定

void main() {
  int score = 72;

  if (score >= 80) {
    print('とても良いです');
  } else if (score >= 60) {
    print('合格です');
  } else {
    print('再提出です');
  }
}

実践2:曜日表示

void main() {
  int day = 5;

  switch (day) {
    case 1:
      print('月曜日');
    case 2:
      print('火曜日');
    case 3:
      print('水曜日');
    case 4:
      print('木曜日');
    case 5:
      print('金曜日');
    default:
      print('土日または不正な値');
  }
}

実践3:座標の形を確認する

void main() {
  var point = [3, 7];

  if (point case [int x, int y]) {
    print('座標は ($x, $y) です');
  } else {
    print('座標データではありません');
  }
}

この 3 つを見ると、

if は条件、switch は候補、pattern は形、 という違いがかなり見えやすくなります。

12. 練習問題

問題1

次のコードは何を表示しますか。

void main() {
  int score = 61;

  if (score >= 60) {
    print('合格');
  } else {
    print('再提出');
  }
}

問題2

次のコードは何を表示しますか。

void main() {
  String weather = 'rain';

  switch (weather) {
    case 'sunny':
      print('晴れです');
    case 'rain':
      print('雨です');
    default:
      print('不明です');
  }
}

問題3

次のコードで、else if が必要な理由を一文で説明してください。

void main() {
  int score = 95;

  if (score >= 90) {
    print('A');
  } else if (score >= 60) {
    print('B');
  } else {
    print('C');
  }
}

問題4

次のコードは何を表示しますか。

void main() {
  var data = [5, 8];

  if (data case [int x, int y]) {
    print(x + y);
  } else {
    print('no');
  }
}

問題5

ifswitchpattern のうち、次の場面に一番向くものを選んでください。

  1. 点数が 60 点以上かどうかを判定したい
  2. メニュー番号 1〜4 に応じて処理を変えたい
  3. List に整数が 2 個入っているか確認し、中身を取り出したい

13. 練習問題の答え

答え1

合格 が表示されます。61 >= 60 が true だからです。

答え2

雨です が表示されます。

weather'rain' なので、その case に一致します。Dart の switch は、値を case に対して評価して一致した本体を実行します。

答え3

90 点以上を先に判定し、その次に 60 点以上を判定したいからです。

もし score >= 60 を先に書くと、95 点でも先にそちらへ当たってしまいます。

答え4

13 が表示されます。data[int x, int y] の形に一致するので、x = 5y = 8 となり、x + y13 です。Patterns は match と destructure を同時に行えます。

答え5

  1. if
  2. switch
  3. pattern

14. まとめ

この節では、条件分岐の三つの顔を学びました。

if は、条件式で分ける基本構文です。

switch は、一つの値を複数候補と比べるときに読みやすい構文です。

そして pattern は、値の「形」を見て、一致確認と中身の取り出しを同時にできる仕組みです。Dart 3 では patterns が正式に入り、switch の case も pattern として扱われるようになりました。

歴史をふり返ると、条件分岐は 1960 年の ALGOL 60 の時代から重要な概念でした。多枝分岐は ALGOL 68 の case-clause や、Dennis Ritchie が文書化した C の switch statement を通じて広く普及しました。さらに pattern matching の考え方は Standard ML が大きく広め、その流れが今の Dart にも入っています。つまり、いま書いている ifswitchpattern は、バラバラの新機能ではなく、長いプログラミング言語の歴史の上に並んでいるのです。

次節では、今度は「同じ処理をくり返す」ためのループ処理を学びます。条件で道を分ける感覚がつかめると、ループの中で条件を使う場面もかなり読みやすくなります。

参考文献

教材トップへ戻る