繰り返し処理

アルゴリズムと繰り返し処理

コンピュータのプログラムの処理には、順次処理(じゅんじしょり)分岐処理(ぶんきしょり)があることはすでに述べました。ここでは、もうひとつの処理、繰り返し処理について説明します。 コンピュータのプログラムのプログラムの処理のことを、一般にアルゴリズムと言いますが、アルゴリズムは、この3つの処理によって記述されています。ここでは、Javaにおける繰り返し処理の記述方法について説明します。

for文

サンプルプログラム

では、まず手始めに繰り返し処理の最も基本的な処理である、for(フォー)文について学んでいくことにしましょう。for文は、{}で囲まれた処理を、指定した条件が満たされるまで繰り返す処理です。 繰り返し処理をループ処理ということから、for()文による繰り返しを、forループとも呼びます。forループは、Javaのプログラムの中で最もよく使われる処理の一つです。しっかりと覚えておきましょう。

まずは、以下のサンプルを実行してみてください。

プロジェクト:Sample401/ファイル名Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Sample401
{
    class Program
    {
        static void Main(string[] args)
        {
            for(int i = 1;i <= 5;i++){
                Console.Write(i+" ");
            }
            Console.WriteLine();
        }
    }
}
実行結果
1 2 3 4 5

for文の書式

プログラムの結果をみると、for()文の{}に囲まれた部分が5回実行されたことが分かります。しかも、iが、1から5に1つづつ増加していたことはわかります。 ではなぜこのような結果になるのでしょう。それを知る前に、まずはfor文の書式をみてみましょう。

for文の書式
for ( 初期化処理 ; 条件式 ; 増分処理 ){
    処理
}

Sample404.javaにあてはめてみると、初期化処理の部分で、まず、「i = 1」としていますから、まず、最初はiの値は1から始まるわけです。条件式は、if文で用いられるものと同じもので、この場合「i <= 5」ですから、iが5以下の場合、この処理は継続されるわけです。 では、増分処理には「i++」と書いてありますが、これは一体なんでしょうか?これは、インクリメントと言って、iの値を1増加させる処理なのです。

以上より、このfor文は、i=1から始めて、iを一つずつ増分させていき、iが5以下ならば{}内の処理を実行することを繰り返し、iが5より大きくなれば、ループから抜ける という処理になるのです。(図4-1)

図4-1.forループの仕組み
Javaのforループの仕組み

インクリメント・デクリメント

for文では、このインクリメントおよび、デクリメントという処理をよく行います。デクリメントとは、インクリメントの反対で、変数の値を1減らす処理です。この処理を表にまとめると、以下のようになります。(表4-1)

表4-1:インクリメント・デクリメントの処理一覧
演算子 呼び名 意味 該当する演算
i++ インクリメント(後置:こうち) 変数の値を1増加させる。 i=i+1;
i+=1;
++i インクリメント(前置:ぜんち) 変数の値を1増加させる。
i-- デクリメント(後置:こうち) 変数の値を1減少させる。 i=i-1;
i-=1;
--i デクリメント(前置:ぜんち) 変数の値を1減少させる。

前置と後置

これをみると、i++と++i、i--と--iはそれぞれまったく同じ意味であるように見えます。しかし、このように前置(ぜんち)後置(こうち)があるのには、それぞれ意味があるのです。例えば、

int a1=1,b1=1,c1=1,d1=1;
int a2,b2,c2,d2;
a2=a1++;
b2=++b1;
c2=c1--;
d2=--d1;

といった処理を行うと、a1~d1,a2~d2はそれぞれ、以下のような値になるのです。

a1 = 2  b1 = 2  c1 = 0  d1 = 0  a2 = 1  b2 = 2   c2 = 1  d2 = 0

となります。a1~d1が、インクリメント・デクリメントで増減する値はそれぞれ変わりませんが、a2~d2は、異なる値となっています。これは、 前置が、演算の修了後、左辺の値に代入されるのに対し、後置が、代入の後に演算を行うということによる違いに基づくものです。インクリメントだけを例にとると、以下のようになります。

インクリメントの処理の内容
a=i++;a=i; i+=1;に該当
a=++i:i+=1; a=i;に該当

デクリメントに関しても、中身は同様です。このような処理は、かつてコンピュータの処理速度がさほど速くなかったり、メモリなどが十分になかったころ、リソースを少しでも節約するために用いられた手法です。しかし、 現在のコンピュータは、処理スピードも、メモリも十分に搭載していることから、こういった処理をプログラム中で書くことはほとんどなくなりました。

様々なfor文の記述方法

これで、基本的なfor文の使い方がわかったことと思います。次に、これを用いて色々な記述方法を見てみましょう。 Sample401.java1の6行目を、表4-2のように、様々な値に変えてみましょう。

表4-2:for文の記述方法の例
記述方法 実行結果 説明
for(int i = 0 ; i < 5 ; i++) 0 1 2 3 4 変数の値を1増加させる。5になると、ループを抜ける
for(int i = -2 ; i <= 2 ; i++) -2 -1 0 1 2 -2から2まで、値を1つずつ増加させる
for(int i = 0 ; i < 10 ; i+=2) 0 2 4 8 変数の値を2づつ増加させる。
for(int i = 5 ; i >= 1 ; i--) 5 4 3 2 1 変数の値を1減少させる。5になると、ループを抜ける
for(int i = 2 ; i >= -2 ; i--) 2 1 0 -1 -2 2から-2まで、値を1つずつ減少させる
for(int i = 12 ; i > 0 ; i-=3) 12 9 6 3 変数の値を3づつ減少させる。0になると、ループを抜ける

この例では、intの場合をとりあげましたが、その他のデータ型でもforループを利用することは可能です。

forの二重ループ

サンプルプログラム

次に、forの二重ループ、もしくは、forのネスト処理を紹介します。この処理は、forループの中に更にforループを記述する二重の処理で、これもよく用いられるテクニックです。

プロジェクト:Sample402/ファイル名Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Sample402
{
    class Program
    {
        static void Main(string[] args)
        {
            //  forの二重ループ
            for(int i = 1;i <= 2;i++){
                for(int j = 1; j <=3 ; j++){
                    int k = i + j;
                    Console.Write(i+"+" + j +  "=" + k +" ");
                }
                Console.WriteLine();
            }
        }
    }
}
実行結果
1+1=2 1+2=3 1+3=4
2+1=3 2+2=4 2+3=5

多重ループ

このプログラムでは、その側の変数iのループが、内側の変数jのループを繰り返しています。それぞれ、3回×2回で、6回のループが実現しています。(表4-3)

表4-3:i,jの関係性
i j i+j
1 1 2
2 3
3 4
2 1 3
2 4
3 5

このようにして、3重、4重のループを作ることも可能です。しかし、現実的に最も多いのは2重ループぐらいまでのようです。

while

サンプルプログラム

ループ処理を行うのは、for文だけではありません。while(ホワイル)文を用いることによっても実現可能です。まずは、以下のサンプルを見てください。

プロジェクト:Sample403/ファイル名Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Sample403
{
    class Program
    {
        static void Main(string[] args)
        {
            int i = 0;
            while(i <= 5){
                Console.Write(i+" ");
                i++;
            }
            Console.WriteLine();
        }
    }
}
実行結果
1 2 3 4 5

プログラムについて解説する前に、まずはwhile文の書式を見てみましょう。

whileの書式
whlie(条件式){
    処理
}

while文の書式

while文は、()内の条件が成り立つ間は、{}内に記述されている処理を繰り返します。つまり、Sample403.javaでは、i<=5という条件が成り立っている間は中の処理が繰り返されることを意味します。 ()内で記述する条件式の記述方法は、if文の場合と同様のものです。

5行目で、iを0で初期化していますから、この段階でwhile文の条件である、「i<=5」は偽ですから、ループ処理に入ります。ループ内で、 iの値を表示すると共に、i++を行うことによって、iの値が増加しています。そのため、i==6となると、i<=5という命題は正しくない、つまり「偽」となります。ループから出ます。(図4-2)(表4-4)

図4-2.whileループの仕組み
whileループの仕組み
表4-4:iと条件の関係性
i 条件式 真/偽
1 1<=5
2 2<=5
3 3<=5
4 4<=5
5 5<=5
6 6<=5

見てわかるとおり、このサンプルは、Sample401.javaと同等の処理です。しかし、for()文と違うのは、インクリメント・デクリメントといった処理や、初期値を設定する処理がループ内に存在しないことです。

do~while

サンプルプログラム

次に、ループ処理の3つ目である、do~while(ドゥ・ホワイル)文について説明しましょう。まずは、次のサンプルを入力してください。

プロジェクト:Sample404/ファイル名Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Sample404
{
    class Program
    {
        static void Main(string[] args)
        {
            int i = 0;
            do{
                Console.Write(i+" ");
                i++;
            }while(i <= 5);
            Console.WriteLine();
        }
    }
}
実行結果
1 2 3 4 5

do~while文の書式

実行結果は、Sample401.java,Sample403.javaと変わりません。(図4-3)ここで出てくる、do~while文の書式は、

図4-3.do~whileループの仕組み
do~whileループの仕組み
do~whileの書式
do{
    処理
}whlie(条件式); ← whileの後にセミコロンがついている。

実は、do~while文は、条件式の判定が後ろについているだけで、while文とまったく同じ働きをします。ただし、こちらは、whileの後に;(セミコロン)がついていますので、注意してください。

while文との違い

ここまでくると、「いったい、whileとdo~whileはどう違うのか?」という疑問をもたれる方もいることでしょう。実は、この二つには大きな違いがあるのです。まずは、以下のサンプルを実行してみてください。

プロジェクト:Sample405/ファイル名Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Sample405
{
    class Program
    {
        static void Main(string[] args)
        {
		    int i,num;
		    num = 1;
	        //  whileループで実行
	        Console.Write("whileで実行:");
	        i = 1;
	        while(i < num){
	            Console.Write("*");
	            i++;
	        }
	        Console.WriteLine();
	        //  do~whileループで実行
	        Console.Write("do~whileで実行:");
	        i = 1;
	        do{
                Console.WriteLine("*");
	            i++;
	        }while(i < num);
        }
    }
}
実行結果
whileで実行:
do~whileで実行:*

whileの方は、何も表示されていません。しかし、do~whileの方は、1つだけ表示されています。これは、whileの場合、iの値はもともと条件を満たしていないので、{}内の処理が実行されないのに対し、 do~whileの場合、まず{}の処理を実行してから条件判定をしているため、仮に条件式を満たしていなくても最低限1度は処理が実行されることによるものです。

無限ループ

サンプルプログラム

最後に、ループ処理でよく出てくる、無限ループという処理について説明しましょう。無限ループとは、その名のとおり、「際限なく繰り返されるループ」です。ここでは、while文を用いたサンプルを紹介します。

プロジェクト:Sample406/ファイル名Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Sample406
{
    class Program
    {
        static void Main(string[] args)
        {
            //  乱数の初期設定
            Random rnd = new Random();
            Console.WriteLine("6が出たら終了");
            //  無限ループ
            while(true){
                int dice = rnd.Next(1, 7);  //  1以上7未満の乱数を発生させる(1から6まで)
                Console.WriteLine(dice);
                if(dice == 6){
                    break;      //  ループから抜ける
                }
            }
            Console.WriteLine("終了");
        }
    }
}
実行結果
6が出たら終了
2
2
3
6
終了

プログラムを実行すると、「6が出たら終了」と言う文字が出力され1から6の乱数が次々と出力されます。1から5の数値だと、そのまま繰り返されますが、6が出ると終了します。

乱数

無限ループについて説明する前に、まずは乱数(らんすう)について説明しましょう。乱数とは、でたらめな数のことで、いわば、さいころを振るように、でたらめな数を発生させる処理のことを、乱数を発生させると言います。C#で乱数を発生させる際には、以下のような手続きをします。

乱数オブジェクトの生成
Random rnd = new Random();

14行目に出ているこの処理は、乱数オブジェクトの生成を行っています。オブジェクトとは何か、ということに関しては、6日目で詳しく説明します。ここでは、乱数を発生させるための手続きだと思ってください。

続いて、この発生させたオブジェクトから、乱数を発生させます。18行目を見てください。

乱数オブジェクトの生成
int dice = rnd.Next(1, 7); // 1以上7未満の乱数を発生させる(1から6まで)

Nextの()の中に、発生させる乱数の範囲を指定します。最初の「1」は、発生させる乱数の最小値です。それに対し、二つ目の「7」は、発生させる乱数を7未満にするという意味です。つまり、Next(1,7)とすることにより、1から6の乱数を発生させるということになります。

そのため、変数diceには、この処理を実行するたびに、1から6のでたらめな数を発生させて代入させることになります。

無限ループとbreak

while(true)とすると、基本的に処理は無限に繰り返されます。これは、条件式が偽の場合は、true、真の場合はfalseをとることを利用した方法で、無限に繰り返されます。ただ、こういった無限ループでも、break(ブレイク)があると、ループから出ることができます。

無限ループは、forでも、do~whileでもできます。いずれも、breakで出ることができます。ただ、無限ループには、故意に行うものと、意図せずにできるものがあります。 故意に用いる場合は、このプログラムのように、必要な箇所にbreakを入れて、ループから出ることができるようになります。 しかし、そうでない場合は、強制的にプログラムを終了させる必要がある場合もあります。

以上で、順次処理分岐処理繰り返し処理という、アルゴリズムの三大要素を記述する方法について説明しました。 しかし、これだけではより複雑な実用プログラムを作るにはまだ不十分です。そこで、次からは、より複雑なプログラムを作るために必要な知識を身につけていくことにしましょう。

練習問題 : 問題4.