ワレコ
ワテがプログミラングを覚えた頃にはFORループくらしか知らなんだ。
例えばBASIC言語の場合ならこんなFORループだ。
FOR I=1 TO 10 STEP 2 PRINT "I=" + I NEXT I
その後、FORTRAN, C, VB.NET, C#, C++ などの言語を覚えたのだが、それらのプログラミング言語の学習の過程で悩んだ事が有る。
それは、冒頭の FOR ~ NEXT ループ以外にいろんなループが出て来るのだ。
具体的には、以下の通り。
forループに関しては、最近のワテは
for(int i=0;i<10<i++){ ... } // C#, C, C++, etc
や
foreach(var item in array){ ... } // C#, etc
をよく使う。特に前者のindex方式がワテ好みだ。
一方、whileループに関しては、
while(){ ... } do{ ... } while() do{ ... } until()
などがあるが、どれを使ったら良いのか分からないので悩んだ時期が有った。
その後のワテは、長年のプログラミング経験を経て現在ではwhile系ループ処理に関しては一種類だけのループ処理を使っている。
この記事では、各種のループ処理を比較しながら、最終的にワテが現在使っているその唯一のループ処理がどれなのか?なぜそれだけで良いのかなどを解説したい。
では本題に入ろう。
ループで処理するデータの準備
以下では配列の中身をループ処理で順番に表示すると言う単純な処理をしている。
正確に言うと配列ではなくてリストList<int>だ。要するに整数型のリスト。
使用言語はC#
そのリストデータは以下の通り。
10個の要素 0, 1, 4, 9, 16, 25, 36, 49, 64, 81 を持つリストを作っておく。
List<int> lst = Enumerable.Range(0, 10).Select(x => x * x).ToList();
このリスト lst のデータを以下では各種のループで出力してみる。
なお、この lst データ自体には深い意味は無い。
最近C#のLINQに凝っていて、こんなやり方を覚えたので使ってみたかっただけだ。
なので、
for (int i = 0; i < 10 ; i++ ) { lst.add(i*i); }
と書いても同じ。
普通の for(int i=0;i<imax;i++){…} ループ
さて、C#の最初のループは普通の for ループだ。
for (int i = 0; i < lst.Count; i++) { Console.WriteLine("i={0}, lst[{0}]={1}", i, lst[i]); }
実行してみる。
先ほど作成した List<int> lst を順番に出力するだけの簡単なC#プログラムだ。
実行結果
--- for loop --------- i=0, lst[0]=0 i=1, lst[1]=1 i=2, lst[2]=4 i=3, lst[3]=9 i=4, lst[4]=16 i=5, lst[5]=25 i=6, lst[6]=36 i=7, lst[7]=49 i=8, lst[8]=64 i=9, lst[9]=81
まあこれは分かり易い。
開始と終了の条件を指定して決まった回数だけループする。
必要なら途中で
if(条件) break;
を入れておけばループの中断も出来る。
while(条件){ … }
次はwhileだ。
int i = 0; while (i < lst.Count) { Console.WriteLine("i={0}, lst[{0}]={1}", i, lst[i]); i++; }
このループも良く知られているが、実はワテはあまり使わない。
実行結果は上のforループの場合と同じなので割愛する。
while(true){ 先頭break }
whileループでワテが良く使うのはこちらだ。
int i = 0; while (true) { if (i >= lst.Count) { break; } Console.WriteLine("i={0}, lst[{0}]={1}", i, lst[i]); i++; }
まあ一つ前の while (i < lst.Count) の条件判定部分を否定形に変えて { … } の中の先頭に持ってきただけだ。
で、while(true) で { … } に入った直後に if(条件)break; するので動作としては 一つ前の
while(条件){ 処理 }
と全く同じ。
まあ while(true)とか for(;;) の無限ループを嫌う人もいるかも知れないが、ワテの場合にはこのループをよく使う。
while(true){ if(条件) break; 処理 }
理由は後述したい。
さて、ループと言えばもう一つある。
do{…} while(条件);
int i = 0; do { Console.WriteLine("i={0}, lst[{0}]={1}", i, lst[i]); i++; } while (i < lst.Count);
こんなやつだ。
親戚には do~Untilと言うのも有る。C#では無いがVBAだったかな。
プログラミングの解説サイトでは、この末尾whileループに関しては必ず一回はカッコ内の処理を実行すると書いてある。
なので取り合えず一回処理したあとで条件判定するならこのループが適しているとも書いてある。
確かにそうなのだが、ワテの場合には、このループは今では絶対に使わない。
理由は何故か?
それは、以下の理由による。
while(true){ 末尾break }と同じだから
int i = 0; while (true) { Console.WriteLine("i={0}, lst[{0}]={1}", i, lst[i]); i++; if (i >= lst.Count) { break; } }
while(true) ループは先ほど登場したが、今の場合には同じwhile(true)ループだがカッコ内の処理の末尾に break の為のif(条件)を入れている。
こうしておくと、これは一つ前に説明した do{…}while(条件); と全く同じ動きになるのだ。
ただし、両者を比較すると条件部分が互いに否定関係になるが、その事自体は何ら問題は無い。
while(条件){ … }ループの欠点
それと、while(条件){ … }ループの場合、丸カッコ()の中にwhile条件を書く必要がある。
ところがプログラミングしている最中に、まだ十分にアルゴリズムが固まっていない場合には、行き成りここで丸カッコ内のwhileループ条件をスラスラ書く事は困難なのだ。
この方式だと、そもそも { … } の処理部分を書く前に while(条件) を書く必要が有る訳だが、どんな処理をするかまだ書いてもいないのに、先に条件を書くと言うのは無理があるとワテは思う。
なので、取り敢えずwhile(true) { … } と書いておいて、お茶を濁すのだ。
そして、その後で{ … } 内の処理を書きながらじっくり考えて、if(条件) breakする条件を書けば良いのだ。
そう言う点でもwhile(true) { … } ループはお勧めだと思う。
ワテお勧めのループまとめ
要するにワテが以下の二つのループは使わずに、
// 1A while(条件){ // 冒頭で判定。処理を一回も実行しない場合もある。 処理 }
// 2A do{ 処理 }while(条件); // 末尾で判定。処理は必ず一回は実行する。
以下の二つのループを使う理由は、
// 1B (1Aと同じ動き) while(true){ // 冒頭で判定。処理を一回も実行しない場合もある。 if(条件) break; 処理 }
// 2B (2Aと同じ動き) while(true){ 処理 if(条件) break; } // 末尾で判定。処理は必ず一回は実行する。
この while(true){…} を一つ覚えておけば、if(条件)break; の位置を変えるだけでどちらのループにも出来るからだ。
あるいは、以下のように
// 3 while(true){ 処理1 if(条件) break; 処理2 }
などとすると、より柔軟性のある記述が可能となる。
ただし、この手法3は混乱し易い場合もあるので、基本はループ内の先頭あるいは末尾でのbreak脱出をお勧めする。
以上がワテ流のループ記述方法だ。
本格的にプログラミングを勉強したい人は有料だけれどこういうオンライン学習サイトもお勧めだ。
プログラミングを独学するのはかなりの忍耐力が必要だ。挫折する人も多い。
それに比べれば、講師の人から教えて貰える学習サイトは遥かに効率よく学べる。ただし有料だけれど。
まとめ
ワレコ
ここで紹介した手法は、あくまでワテ流なので世間一般の常識かどうかは未確認だ。
ワテの場合、条件後置きの do{…}while(条件); を覚えた頃には、プログラミングをしていて何らかのループ処理の場面に来た時に、
while(条件){ // 1A ... }
do{ // 2A ... }while(条件);
このどっちのループを使うべきかで悩んだ。
1A方式で書いていて、いや待てよ。一回は処理したいので2Aを使うべき?
などと混乱して、ループの記述を変えた経験が何度もある。
そう言う場合は、アルゴリズム自体が頭の中で十分に完成していないので、即席でループ構造を変更したりすると益々泥沼状態になる(ワテの経験に基づく)。
しかしながら、上で説明した
// 3 while(true){ 処理1 if(条件) break; 処理2 }
方式のループを使う事に統一した今では、そんな事で悩む必要は無くなった。
必要に応じて if(条件)break; の位置を変えるだけなので、どっちのループを使うべきかなどと言う横道に逸れて思考が中断する事なく、本来のプログラミング作業に集中出来る。
と言う事で、あながちヘンテコな手法では無いと思っている。
まあ要するに、「while(条件)」ループ処理では「~の条件になるまで作業しなさい」だが、「while(true)if(条件)break」ループ処理では「~の条件になったら作業をやめなさい」となる。
例えて言えば、子供に対して
「夕方5時になるまでは外で遊んでもいいよ」
「夕方5時になったら遊ぶのをやめて家に帰って来なさいよ」
のどちらを言っても同じ意味だが、ワテは後者の方が分かり易いと思う。
ご意見、コメントなどありましたらお知らせ下さい。
有名な本だがワテは読んだ記憶が無い。
読んだかも知れないが全く記憶にない。
プログラムは芸術なのか?
コメント