ゆるおたノート

Tomorrow is another day.

【GoogleAppsScript】制御構文で処理から抜け出すパターンが多すぎるので整理してみた話。

最近、VBAと共にGASをお勉強しています。

ノンプロ研の「今週のお題」チャンネルを眺めていたところ、ちょうど新しいお題が更新されました。

お題は、FizzBuzz問題

FizzBuzz問題を色んなパターンでどうぞ!
→1から100までの数を出力、ただし3の倍数のときは数の代わりに「Fizz」、
5の倍数のときは「Buzz」、3と5の両方の倍数の場合には「FizzBuzz」と出力
(ノンプロ研 #12_今週のお題 チャンネルより)

プログラムのお勉強では定番だそうです。

ちょうどif文やfor文などの制御構文を勉強したばっかりでしたが、
勉強とは言っても写経つづきで実践問題はまだ試してなかったし、
「お、これなら書ける…?今がこの時!」と息巻いて挑戦してみました。

が、ループをどう書くかで悩んでしまいました。

繰り返すとか繰り返さないとかしゃらくせぇ!勝手にしやがれコノヤロー!!ヽ(`Д´)ノ

…とは思っても、残念ながらコンピュータにはそんな言葉通じないので、
(例によって時間は経ってしまいましたが)整理してみました。

最初のソースコード

お題は、先述のFizzBuzz問題

if文とfor文を組み合わせて、初心者コードで書いてみました。
下記は、その初稿です。

function FizzBuzz() {
  for (var i = 1; i <= 100; i++) {
    if (i % (3 * 5) === 0) {
      Logger.log('FizzBuzz');
      continue;
 
    } else if (i % 3 === 0) {
      Logger.log('Fizz');
 
    } else if (i % 5 === 0) {
      Logger.log('Buzz');
 
    } else {
      Logger.log(i);
 
    }
  }
}

JavaScriptに慣れ親しんでいる方はお気づきかと思いますが、こちら↑には間違いがあります。

構文エラーは出ないし、処理も想定通り動いてくれたのですが、正しい書き方ではありません。
つまり、プログラミングの敵である「何故かちゃんと動く」コードです。

書いている間も「for文の内側のif文で条件を満たしたときにどこで次のiに進むのか??」で混乱しつつ、
自信が無いながらも提出してみたところ、ノンプロ研のGAS番長さんにコメントをいただきました。
いつもありがとうございます!!

「5行目のcontinueいらんはず~!」

そう、if文の場合は、どこに書くかが問題ではなくてcontinue文自体不要なのです。
混乱した原因は、同じ制御構文の中でも、使用するモノによって必要なステートメントが違ったり
ステートメント自体不要だったりすることでした。

同じ過ちを繰り返しては困るので、処理から抜け出す表現の違いを
以下に整理してまとめてみます。

参考文献

今回の参考文献と出典元はこちら。いつもお世話になっております。

それぞれの構文の詳細はこちら↑をご参照ください。

if文

概要

通常の条件分岐。

最初のtrueブロックの処理が終わり次第、抜け出す

条件式1、条件式2、~と順番に判定し、最初に条件式がtrueだったブロックが実行される。

if (条件式1) {
  //条件式1が true の場合に実行
 
} else if (条件式2) {
  //条件式2が true の場合に実行
 
~~~
~~~
 
} else {
  //上記すべての条件式が false だった場合に実行
 
}

switch文

概要

条件式内で、左辺(式)は共通で右辺(値)だけが変わっていくような場合は、
この方がif文よりシンプルに書ける。

trueブロックを順に実行し、break文で抜け出す

式に該当するcase節以降のすべての処理を実行する。

switch (式) {
  case 値1:
    //式 === 値1 だった時の処理
    break;
 
  case 値2:
    //式 === 値2 だった時の処理
    break;
 
~~~
~~~
 
  default:
    //式が上記すべての値に合致しなかった時の処理
}

なお、break文が無い限りは、ブロックの途中で抜け出せない。

while文

概要

条件式を満たしている間は、処理を繰り返す。回数は条件式の内容により変化するので不定

条件式がfalseになったら、処理を止めて抜け出す

条件式がtrueの間はブロック内の処理を繰り返し、条件式がfalseになったらwhileループを抜ける。

while (条件式) {
  //条件式が true の間、繰り返し実行される
}

※(注意)無限ループの例

while文は、whileループを抜けるように組まなければならない。

//▼サンプル4-4-2(p.111)より
function myFunction() {
  var x =0;
  while (x < 100) {
    x *= 3; //0に何回3を掛けても、xは増えない
    Logger.log ('xの値:%s', x);
  }
}

上記のような例では、永遠に処理が終わらない。

GASの場合は"関数myFunctionを実行中…"が消えなくなるので、キャンセルで消して処理を強制終了すること。
(GASなら制限時間が来れば自動終了するけど、サーバーへ余計な負担になるので…)

for文 と for...in文

概要

  • for文とは?
    カウンタ変数(通常はイテレータiを使って回数を制御しつつ、繰り返す。

  • for...in文とは?
    オブジェクト内のすべてのプロパティについて繰り返し処理を行う。

抜け出し方は、どちらも同じ。

break文で処理を止めて抜け出す

break文と、for文、if文とを組み合わせることで、ループの終了条件を増やすことができる。

for (初期化式; 条件式1; 増減式) {
  if (条件式2) {
    break; //条件式2が true になったらループを中断してfor文の外へ
  }
 
  //(条件式2が true になると飛ばされる)
}

continue文で次にスキップする

条件式を満たした場合のみループ処理をスキップして、次の繰り返しに移行する。

for (初期化式; 条件式1; 増減式) {
  if (条件式2) {
    continue; //条件式2が true なら、for文内の処理は1回スキップして次の繰り返しへ
  }
 
  //(条件式2が false の時のみ実行される)
}

ループにラベルを付けて階層を指定する

ループや条件分岐をネストした場合、ラベルの付与をすることで
中断(break文)したりスキップ(continue文)したりする階層を指定することができる。

/* for文 その1==================== */
ラベルA : //※ここは【半角スペース+コロン】
for (初期化式1; 条件式1; 増減式1) {
 
  /* for文 その2---------------------------------- */
  for (初期化式2; 条件式2; 増減式2) {
    if (条件式3) {
      break ラベルA; //※ここは【セミコロン】
      //条件式3が true になったら、for文①(ラベルAのついたfor文)のループを中断してfor文①の外へ
    }
 
    //(条件式3が false の時のみ実行される)
  }
  /* ------------------------------------------ */
 
}
/* ============================== */

お勉強まとめ。

以上より、今回のお題の正解(の1つ)も記載しておきます。

function FizzBuzz() {
  for (var i = 1; i <= 100; i++) {
    if (i % (3 * 5) === 0) {
      Logger.log('FizzBuzz');
 
    } else if (i % 3 === 0) {
      Logger.log('Fizz');
 
    } else if (i % 5 === 0) {
      Logger.log('Buzz');
 
    } else {
      Logger.log(i);
 
    }
  }
}

他の方の回答やネットを見ると、まだまだ書き方があるみたいです。
クラスとか二項演算子とか。私にはまだ難しいですが…

余談ですが、間違ったコードを「成果物」として公開するのは、本来良いことでは無いと思います。
でも、チームプレイではない孤独なノンプログラマーとしては、上述のように直接フィードバックを頂けるのは
とても有難いことですし、私みたいなモノグサ人間でもちゃんと復習するキッカケにもなりました。

なので、私のようにビビりな方がもし居れば、勉強中のうちにダメなところもどんどんアウトプットして、
ガンガン叩いてもらって笑、今のうちに弱点を潰して行きましょう!…という受け売りです。笑

そして、早く私も誰かを助けられるようになりたい。