今回は、複数のデータに対して反復して処理を行い、その結果をHTMLの表で出力する演習を行います。
プログラムの処理の流れは、順次・選択・反復の組み合わせでできています。
前回の「選択」に続き、今回は、「反復(ループ処理)」を記述する制御構文(for文、while文、foreach文)について説明します。
また、変数とスコープについての説明も行います。
選択(if文)についてはこちらの記事を御覧ください。
同じ種類の要素が複数並んだ構造としては、Webアプリケーションであればユーザ情報・注文情報・商品情報、ゲームであれば敵キャラクタ・アイテムなど考えてもらえればわかりやすいかと思います。
YouTubeの動画でも解説しているので、こちらもぜひ御覧ください。
演習1:複数のデータを表で表示
演習プログラムとポイント
今回は以下のようなプログラムを作ります。
- 整数が並んだデータ(整数配列)に対して反復処理を行い、データをHTMLの表で出力する。
- 整数データの平均値も計算して出力する。
配列というデータ構造を使って、整数が並んだデータをプログラム内で用意します。
「for文」という制御構文を用いて、配列に対する反復処理を記述します。
以下がプログラムコードです。
int[] numbers = new int[] { 10, 5, -8, 0, 1 };//★(a)整数配列を作成
Console.WriteLine("<html><body>");
Console.WriteLine("<table border=\"1\">");
int sum = 0;
//★(b)反復を行うfor文
for(int i = 0; i < numbers.Length; i++)
{
Console.WriteLine($"<tr><td>配列の{i}番目</td><td>{numbers[i]}</td></tr>");
sum += numbers[i];
}
Console.WriteLine("</table>");
double average = (double)sum / numbers.Length;//★(c)型キャスト
Console.WriteLine($"<p>平均値は{average:F2}</p>");
Console.WriteLine("</body></html>");
最初に(a)の部分で、int型の配列を作成しています。
そして、(b)の部分ではその配列へfor文で反復処理を行い、配列の何番目にどの値が格納されているかという情報をHTMLのTableタグを用いて出力しています。
加えて、反復処理の中で配列の各要素を合計し、最後に「合計値/要素数」を計算することで平均値を計算して出力しています。
(c)の部分では、型キャストという仕組みを使っています。
それでは、それぞれのポイントについて説明します。
ポイント1:配列の基本
配列の最も基本的な部分だけを説明します。
配列は特定の型の値が複数順番に並んでいるデータ型です。int[]
というように「X[]」と記述すると、それは「X型の配列」の型を表します。
「new int[] { 10, 5, -8, 0, 1 }
」というようにnew演算子を用いて「new X[] {x1, x2, ... xn}
」と記述すると、「{x1, x2, ... xn}
」の要素をもつ配列を作成できます。
例えば、int型のデータとint型の配列(int[]型)のデータは以下のようなイメージです。
配列の要素番号は0から始まるので注意してください。配列の要素番号を添字ともいいます。
配列におけるi番目の要素の参照や代入は、numbers[i] のように変数名の後に[要素番号]をつけて行います。([]をインデクサといいます)
配列の要素数(長さ)は、numbers.Lengthのように「変数名.Length」で取得できます。
ポイント2:反復
for文は以下のような構文になっています。
for(①カウンタ変数の初期化; ②条件式; ④カウンタ変数の更新)
{
③反復して行う処理
}
for文は以下のような処理の流れになります。①を最初に一度だけ行い、あとは②、③、④を反復して行います。
- ①カウンタ変数の初期化
- ②条件式が成立していれば③へ、不成立ならば反復を終了
- ③反復して行う処理
- ④カウンタ変数の更新、②へ
カウンタ変数とは、反復の回数を数えるための変数です。
演習プログラムの(b)部分、for文をもう一度みてみましょう。
//★(b)反復を行うfor文
for(int i = 0; i < numbers.Length; i++)
{
Console.WriteLine($"<tr><td>配列の{i}番目</td><td>{numbers[i]}</td></tr>");
sum += numbers[i];
}
ここでは、iがカウンタ変数ですね。
条件式は、「i < numbers.Length
」となっているため、カウンタ変数iの値がnumbers配列の要素数未満である限り、反復処理が行われます。
カウンタ変数更新は「i++
」で行っており、iを1ずつ増やしています。
numbersの要素数は5なので、iが0からスタートし、i=0,1,2,3,4とnumbersの各要素に対して処理を行うことになります。(配列の要素番号は0からスタートですね)
反復処理では配列の要素番号と要素の値について、以下のように出力しています。
<tr><td>配列の0番目</td><td>10</td></tr>
<tr><td>配列の1番目</td><td>5</td></tr>
<tr><td>配列の2番目</td><td>-8</td></tr>
<tr><td>配列の3番目</td><td>0</td></tr>
<tr><td>配列の4番目</td><td>1</td></tr>
「sum += numbers[i]
」としてsumへ配列の各要素の値を加え、合計値の計算も行っています。
この合計値は、最後に配列要素の平均値を算出するために使います。
ポイント3:キャスト式
(c)の部分では、合計値を配列要素数で割ることで平均値を算出しています。
double average = (double)sum / numbers.Length;//★(c)型キャスト
「sum / numbers.Length
」をそのまま計算すると整数型同士の除算となり、7/2=3という結果となってしまい、3.5となりません。
そこで、キャスト式を用いて、sumに格納された整数値を浮動小数点の値へと明示的に型変換します。
キャスト式を使って「(X)式」と記述すると、式の値がX型へ変換されます。こうすることで、「浮動小数点数型/整数型」の計算となるため、計算結果も浮動小数点数型となります。
型変換については以前の記事でも、「Convert.To型名()」の命令を使う方法を紹介しましたね。
型変換には、Convert.To型名()のような命令を使って変換する方法・演習コードで書いたように明示的にキャスト式で変換する方法・暗黙的に変換する方法など、いくつかの方法があります。
詳しくは、キャストと型変換 (C# プログラミング ガイド)を参考にしてみてください。
プログラムの実行結果
プログラムの実行結果は以下のようになります。
<html><body>
<table border=”1″>
<tr><td>配列の0番目</td><td>10</td></tr>
<tr><td>配列の1番目</td><td>5</td></tr>
<tr><td>配列の2番目</td><td>-8</td></tr>
<tr><td>配列の3番目</td><td>0</td></tr>
<tr><td>配列の4番目</td><td>1</td></tr>
</table>
<p>平均値は1.60</p>
</body></html>
このHTMLをブラウザで表示すると以下のようになります。
配列の各要素が表示され、平均値も浮動小数点数型になっていますね。
講義1:反復
C#で反復を記述する構文について説明します。
for文
for文については、既に演習のポイントで説明したので、ここでは補足をします。
入れ子
if文など他の制御構文と同様に、for文も入れ子にできます。
以下はfor文を入れ子にして九九の計算を行う例です。
for(int i = 1; i <= 9; i++)
{
for(int j = 1; j <= 9; j++)
{
Console.WriteLine($"{i} × {j} = {i*j}");
}
}
次のような出力になります。
1 × 1 = 1
1 × 2 = 2
1 × 3 = 3
1 × 4 = 4
1 × 5 = 5
…
9 × 8 = 72
9 × 9 = 81
break文
for文など反復を行う処理において、反復処理中にループから抜け出したいときには、break文を使います。
以下は、配列の何番目に0があるかを調べるプログラムです。
int[] numbers = new int[] { 1, 0, 4, -10, 2 };
for (int i = 0; i < numbers.Length; i++)
{
if (numbers[i] == 0)
{
Console.WriteLine($"{i}番目に0を見つけました");
break;
}
}
このプログラムでは、配列要素に0を見つけるとbreak文を使ってループを抜けます。
このように、残りの反復処理を行う必要がなくなった場合などにbreak文を使うと便利です。
continue文
反復処理中にその処理をスキップし、カウンタ変数を更新して次の反復処理を行いたい場合はcontinue文を使います。
以下は、配列要素のうち正の数(1以上の数)のみ出力するプログラムです。
int[] numbers = new int[] { 1, -2, 4, -5, 7 };
for (int i = 0; i < numbers.Length; i++)
{
if (numbers[i] <= 0)
{
continue; //0以下の数なら以降の処理をスキップ
}
Console.WriteLine($"{numbers[i]}は正の数");
}
この例は以下のように、continueを使わずに書けますね。
int[] numbers = new int[] { 1, -2, 4, -5, 7 };
for (int i = 0; i < numbers.Length; i++)
{
if (numbers[i] > 0)
{
Console.WriteLine($"{numbers[i]}は正の数");
}
}
この例ならば、このようにcontinue文を使わずに書いたほうが素直でわかりやすいですね。
上述した例のようにif文などで代替できる場合は、continue文は使わずに書いた方がプログラムの流れはわかりやすくなります。
入れ子構造、break文、continue文は、この後出てくるwhile文、foreach文でも同様に使えます。
多用はしないほうがよいでしょう。
while文
while文は以下の構文です。
while(①条件式){
②反復処理
}
次のように条件式の確認と反復処理を繰り返します。
- ①条件式が成立していれば②へ。不成立ならば反復処理を終了する
- ②反復処理を行う。①へ。
演習1のプログラムを、for文の代わりにwhile文で書いてみます。indexがカウンタ変数ですね。実行結果はもとのプログラムと全く同じです。
int[] numbers = new int[] { 10, 5, -8, 0, 1 };
…
int index = 0;
while (index < numbers.Length)
{
Console.WriteLine($"<tr><td>配列の{index}番目</td><td>{numbers[index]}</td></tr>");
sum += numbers[index];
index++;
}
…
一方で、反復の回数が事前にわからず反復処理を行ってみないと回数がわからない、という場合はwhile文を使うことが多いです。
while文に似た制御構文としてdo文(do while文)もあります。
do文では条件が成立しなくても、最初に必ず一回は反復処理を行うという特徴があります。
反復ステートメント – for、foreach、do、while(C#ガイド)のdoステートメントの項目も参考にしてみてください。
foreach文
foreach文は以下の構文となります。
foreach(要素の型 変数 in 要素を列挙できる型)
{
反復処理
}
foreach文では配列など要素を列挙できる型に対して、その要素を列挙し、各要素に対して反復処理を行います。
演習1のプログラムを少し変更し、配列の要素番号は出力せず要素のみをテーブルで出力するプログラムにおいて、foreach文は以下のようになります。
int[] numbers = new int[] { 10, 5, -8, 0, 1 };
…
foreach (int number in numbers)
{
Console.WriteLine($"<tr><td>{number}</td></tr>");
sum += number;
}
…
このプログラムではnumbersの各要素に対して反復処理を行っています。
実行結果は以下のようになります。
<html><body>
<table border=”1″>
<tr><td>10</td></tr>
<tr><td>5</td></tr>
<tr><td>-8</td></tr>
<tr><td>0</td></tr>
<tr><td>1</td></tr>
</table>
<p>平均値は1.60</p>
</body></html>
for文と異なり、foreach文では、例えば配列の要素番号は気にせず要素のみについて反復処理を行いたいといったときに使います。
配列だけではなく、コレクションと呼ばれるリスト型や辞書型といったデータ構造についてもその要素を列挙することができ、むしろそこで真価を発揮します。
コレクション型についてはまた別途紹介します。
講義2:変数とスコープ
前回学んだ選択や、今回学んだ反復の制御構文ではブロックや、その入れ子の構造がでてきました。
なので、ここで変数とスコープについての話をしておきます。
実は、宣言した変数はプログラムのどこからでも参照できるというわけではなく、参照できる範囲が決まっています。この範囲のことを変数のスコープといいます。
あるブロック内で宣言した変数はそのブロック内でしか参照できません。
以下が変数とそのスコープの例です。
int x = 1;//xは一番外側にあるので、このコードのどこからでも参照できる
if(...){
int y = 2;
for(...){
int z = 3;
//ここでは、x,y,z全てを参照できる
}
//ここでは、x,yのみ参照できる(zは参照できない)
}
else{
//ここではxのみ参照できる(y,zは参照できない)
int y = 4; //なので、yという名前で変数宣言は可能
//ここでyを参照すると、elseブロックのyが参照される
}
ちなみに、for文のカウンタ変数については、forのブロック内のスコープとなります。
for(int i = 0; i < 10; i++)
{
//ここでは、iを参照できる
}
//ここではiを参照できない
仮にスコープが存在せず、宣言した変数がプログラムのどこからでもアクセスできてしまったら、簡単に名前が衝突するようになりプログラムをとても書きにくくなります。
まとめ
今回は、反復を行うfor文、while文、foreach文、そして変数とスコープについても学びました。
これで、配列のようにたくさんの要素が並んでいるデータに対して、反復処理を行うことができるようになりました。
プログラムの構造を構成する順次・選択・反復について、一通り主要なC#の構文を学んだことになります。
これからは、いよいよクラス、メソッドなどオブジェクト指向についての説明に入っていきます。
引続き、C#やプログラミングの考え方を一緒に学んでいきましょう!