September 30, 2022

【Dart】Labelを使ってGoto

DartにはLabelがあります。Cでいうgotoです。KotlinやSwiftにもLabelがあるので、モダンな言語には大体備わっている機能かもしれません。
DartのLabelはCのgotoのように任意の場所に飛べるのではなく、主にループから抜け出す処理等で使います。

DartではSwitch文でもラベルを使うのでまずはその使い方を見てみましょう。

Switch

色のEnumがあるとします。

enum Color {
  red,
  yellow,
  blue,
  lightBlue,
  pink,
}

普通に分岐するとこうなります。

final color = Color.red;
switch (color) {
  case Color.red:
    print('あか');
    break;
  case Color.yellow:
    print('きいろ');
    break;
  case Color.blue:
    print('あお');
    break;
  case Color.lightBlue:
    print('みずいろ');
    break;
  case Color.pink:
    print('ぴんく');
    break;
}

// あか

例えばred, yellow, blueのときに同じ処理を行いたい場合は break を省略して、こう書けます。

final color = Color.red;
switch (color) {
  case Color.red:
  case Color.yellow:
  case Color.blue:
    print('あか、き、あお');
    break;
  case Color.lightBlue:
    print('みずいろ');
    break;
  case Color.pink:
    print('ぴんく');
    break;
}

// あか、き、あお

blueでは「あお」を出力して、lightBlueでは「あお」と「みずいろ」を出力したい場合はどう書くでしょう?
Dartでは、case文に必ずbreakを書かないといけないため、このような書き方はできません。

// NG
switch (color) {
  case Color.red:
    print('あか');
    break;
  case Color.yellow:
    print('きいろ');
    break;
  case Color.lightBlue:
    print('みずいろ');
  case Color.blue:
    print('あお');
    break;
  case Color.pink:
    print('ぴんく');
    break;
}

こういう場合にラベルを使います。

ラベルは [label name]: の形で定義します。
caseの最後にbreakではなくcontinue [label name];を書くことで両方のcaseを実行できます。

final color = Color.lightBlue;
switch (color) {
  case Color.red:
    print('あか');
    break;
  case Color.yellow:
    print('きいろ');
    break;
  blueLabel:
  case Color.blue:
    print('あお');
    break;
  case Color.lightBlue:
    print('みずいろ');
    continue blueLabel;
  case Color.pink:
    print('ぴんく');
    break;
}

// みずいろ
// あお

For-Loop

もう1つはfor-loopでよく使います。 よくあるのは2重ループで内側のループで外側のループも終了するパターンです。

この例では、jが3より大きくなったら内側のループが終了して、外側のループの処理になります。

for (var i = 0; i < 5; i++) {
  print("\n---------");
  print("Outer loop [i]: ${i}");

  for (var j = 0; j < 5; j++) {
    if (j > 3) {
      break;
    }

    print("Inner loop [j]: ${j}");
  }
}

// ---------
// Outer loop [i]: 0
// Inner loop [j]: 0
// Inner loop [j]: 1
// Inner loop [j]: 2
// Inner loop [j]: 3
// 
// ---------
// Outer loop [i]: 1
// Inner loop [j]: 0
// Inner loop [j]: 1
// Inner loop [j]: 2
// Inner loop [j]: 3
// 
// ---------
// Outer loop [i]: 2
// Inner loop [j]: 0
// Inner loop [j]: 1
// Inner loop [j]: 2
// Inner loop [j]: 3
// 
// ---------
// Outer loop [i]: 3
// Inner loop [j]: 0
// Inner loop [j]: 1
// Inner loop [j]: 2
// Inner loop [j]: 3
// 
// ---------
// Outer loop [i]: 4
// Inner loop [j]: 0
// Inner loop [j]: 1
// Inner loop [j]: 2
// Inner loop [j]: 3

内側のループでjが3より大きくなったら外側のループも終了したいとします。
その場合、Labelを使わないとうまく書けません。

ラベルを任意の場所に定義します。今回はfor-loopの直前

outerLoop:
for (var i = 0; i < 5; i++) {
  print("\n---------");
  print("Outer loop [i]: ${i}");

  for (var j = 0; j < 5; j++) {
    if (j > 3) {
      break outerLoop;
    }

    print("Inner loop [j]: ${j}");
  }
}

// ---------
// Outer loop [i]: 0
// Inner loop [j]: 0
// Inner loop [j]: 1
// Inner loop [j]: 2
// Inner loop [j]: 3

この例では、jが3より大きくなったらlabelの位置にジャンプするため1回目の外側のループで処理が終了します。
アプリケーションの処理を書いていると意外とこのような場面に遭遇することがあるので、labelの使い方を覚えていると得することがあります。 ちなみにbreakではなくcontinueにすると先程の例と同様に5回外側のループを回します。

© AAkira 2023