どうも。つじけ(tsujikenzo)です。今日は『for文とは何か(歴史編)』です。まとめというかちょっとした考察になってしまったかもしれません。”ちょっとした”というのは本気でfor文のことを書くと本が一冊書けてしまうからです。
この記事はノンプロ研GAS初級講座7期の卒業LTとして発表した内容です。シリーズでお伝えしてきました補講もこれで最終回となります。
対象となる読者
Javascript(私の場合ほぼGASですが)でfor文やWhile文を書く時に、ここletだっけ?constだっけ?となるのはあるあるですが、完璧に理解してる方はスルーしてもらって大丈夫です。とりあえずletかconstなのか暗記する早見表を探してる方もスルーしてもらって大丈夫です。
今日はプログラミングの『繰り返し』について、その歴史と共に深掘りしてみたいと思います。まずはこのコード(実際に動きます)どんなログが出力されると思いますか?
let i = 0;
for (; ;){
const x = i;
console.log(i);
i = i +1;
if(i > 100) break;
}
歴史
プログラミング言語がどうやって動いているのか理解したい時は、その歴史を紐解いてみるのが近道です。歴史とは「始めは単純な命令で良かったんだけど、この方が効率的だし楽だよね」と人間の手で機能を加えられてきた歴史です。
単純な命令というのは「低水準言語」と呼ばれるアセンブラとマシン語のことです。最初はパソコンに対して「動け!」という命令を出すだけでした。(それだけでも大変なこと💦)
やがて動くことが確実になって「そしたらこんな書き方ができたらもっと楽になるよね?」と先人達がアップデートを積み重ねてきた切磋琢磨の結晶が、現在の私たちが書いているプログラミング言語になっています。
for文なんて無かった
なので、最初はfor文なんて無かったんです。必要な命令は必要な数だけ記述していました。(以後、特に明記しない場合は現代のJavaScriptで表現しています。実際の原文(言語)は異なります。)
console.log(0);
console.log(1);
console.log(2);
…
console.log(100);
ただし、100行命令文を書くなんて、人間にとってもパソコンにとっても大変です💦
gotoの発明
天才はいつの時代にもいます。繰り返しているなら『起点を決めてそこにジャンプすれば楽じゃない』と。
(これは動かないです)
start:
i = 0;
console.log(i);
i = i + 1;
goto start;
まぁこれだと無限にループするので、「ある条件を迎えたらループを抜ける」という条件も加えましょう。
(これは動かないです)
i = 0;
start:
console.log(i);
i = i + 1;
if(i < 100) goto skip;
goto start;
skip:
これで0から100までログ出力するコードが書けました。ただ、段々コードが長くなってきましたし「これは便利だからみんなが使いやすいコードに置き換えられたら楽だよね」ということになります。
while文の登場
さきほどのstart:から始まるgoto文をwhile()という構文にしてしまえ!となりました。
i = 0;
while (i < 100){
console.log(i);
i = i + 1;
}
皆さんが知っているWhile文はこのように誕生しました。
while文のletとconst
while文に突入する前にカウント変数iをletで宣言するのは、まず0に初期化した後に、ループの中で再代入されるからです。(let i = 0;はループが始まる前の宣言なのでループ外ですよね)
let i = 0;
while (i < 100){
console.log(i);
i = i + 1;
}
ループの中はconst
goto文で確認したstart:から始まる処理は常にご新規です。while文でいう{}ですね。常に新規のブロックスコープを生成していますので、再代入で使うletではなくconstで問題ないのです。(ループの中で再代入をするような処理はもちろんletを使いますので、誤解のないように💦)
let i = 0;
while (i <= 100){
const x = i;
console.log(x);
i = i + 1;
}
break
{}の中でbreakを書くと、条件式がtrueになるのを待たずにループを抜けます。
let i = 0;
while (i <= 100){
const x = i;
console.log(x);
if (i === 50) break;
i = i + 1;
}
//0~50までログ出力される
continue
{}の中でcontinueを置くと、現在の処理{}を終了して、次の処理{}を実行します。if文(条件式)と合わせて使われることが多いです。flag判定にもよく使われますね。
let i = 0;
while (i < 100){
const x = i;
i = i + 1;
let flag = true;
if (x % 2) flag = false
if (flag === true) continue;
console.log(x);
if (i === 50) break;
}
//49までの奇数がログ出力される
もうこれで、繰り返しに関する処理はwhileに任せてよさそうです。ある条件式の中で処理を飛ばすcontinueやループを終了させるbreakもありました。
しかし、だんだんコードが長くなってきましたよね💦 カウント変数i = i +1も最終行に置いたり、前半に置いたり、行方不明になりそうです。
for文の登場
天才は言います。「もうこの、カウント変数を宣言して、カウント変数が条件に合う時だけ、処理{}をまわすfor文ってのを作ったから。みんな楽でしょ。」
for (let i = 0; i <= 100; i++){
console.log(i);
}
皆さんが知っているfor文はこのように誕生しました。
条件式の省略
for (let i = 0; i <= 100; i++) console.log(i);
これはよく見かけるfor(条件式)文ですが、実は条件式は全て省略することができます。for文の公式リファレンスについてはこちらで記事を書きました。(今は読まなくて大丈夫です)
ただし、省略する代わりに他の場所でカウント変数を定義したり、条件判定を記述する必要があります。このような感じです。
let i = 0;
for (; ;){
const x = i;
console.log(i);
i = i +1;
if(i > 100) break;
}
これ、一番最初に見て頂いたコードと同じですよね。
while文とfor文は同じ
実はwhile文もfor文も中身は同じで、ただ見た目が違うだけなんです。
let i = 0;
while (i <= 100){
const x = i;
console.log(x);
i = i + 1;
}
let i = 0;
for (; ;){
const x = i;
console.log(i);
i = i +1;
if(i > 100) break;
}
プログラミング言語を設計するってパズルのようで面白いですよね。何も難しいことはなく、for文とは「繰り返しを楽にする」という目的があってアップデートを繰り返されてきた歴史のある構文なのでした。
for (let i = 0; i <= 100; i++){
console.log(i);
}
[おまけ]foreach構文の登場
そしてfor文はさらに発展します。「ループする回数が決まってるなら、わざわざカウント変数で制御しなくても『全部回す!』でいいんじゃない?」と言い出した天才がいます。foreach構文です。(GAS派の人にとってはfor of文と呼んだ方が馴染み深いかもしれません。)
for of文についてはこちらに書きました。なぜconst宣言が使えるのかという話に軽く触れていますが、前述した通り、for文の処理{}は常にご新規様ですので、for of文の条件式の中での変数定義はconstで問題ありません。
const values = [1,2,3,4,5];
for (const value of values){
console.log(value);
}
まとめ
以上で「for文についてまとめ(歴史編)」をお送りしました。「for of文でconst宣言が使えるのはなぜなのか?はGAS中級講座が開かれる際にでも執筆しようと思います。「反復可能オブジェクト(イテラブルオブジェクト)とは何か」ということですね。
さて、ノンプロ研GAS初級講座7期を受講頂いた皆さん、お疲れ様でした。あっという間の2カ月でしたが共に学ぶことができて大変光栄でした。私もたくさん新しいことにチャレンジができましたし、また改めて「基礎って大事なんだな」と思う時間を過ごすことができました。
また分からないことが出てきた時はいつでもTwitterやSlackで声を掛けてください。そして今度は皆さんが社内勉強会やノンプロ研で講師やTAになる番です。スライドや私の補講ブログはジャンジャン丸パクりしてください。皆さんの更なるご活躍を見守っていきたいと思います。TAの橘先生、ホストのタカハシさんもお疲れ様でした!