回れ回れよグルグルと(ループ文)
今日は、同じことを何回も繰り返してもらいます。
でも大丈夫、皆さんがやるのは、
ほんの数行のプログラムを書くことだけです。
それだけで、何回も、何十回も、いや何万回も同じことが繰り返せます。
ループ文をマスターできれば!
- ループ文がない世界
- ループ文がある世界
- ループ文は4種類
- foreach文
- while文
- for文
- do-while文
- ループよ、さらば(break)
- 次の回にスキップ(continue)
- ネストは浅く
- 実習ループ文
- まとめ
ループ文がない世界
ループ文がない世界で、繰り返しのあるプログラムを書くにはどうすればよいでしょうか。
一言でいうと、大変、大変、大きな労苦を伴います。
試しに、ループ文のない世界で、配列の各要素をすべて表示するプログラムを書いてみましょう。
// 7匹の哺乳類がいる配列を定義
string[] mammals= {"dog", "cat", "pig", "monkey", "wolf", "deer", "sheep"};
// 0番から6番まで7匹の哺乳類をすべて画面上に表示
Console.WriteLine(mammals[0]);
Console.WriteLine(mammals[1]);
Console.WriteLine(mammals[2]);
Console.WriteLine(mammals[3]);
Console.WriteLine(mammals[4]);
Console.WriteLine(mammals[5]);
Console.WriteLine(mammals[6]);
大変でしたが、なんとかできましたね。
では次に、ユーザーから入力がある限り、『ユーザーから入力がありました』と表示するプログラムを書いてみましょう。
// 1回目
// Console.ReadLine()は、コンソール画面から入力された文字列
// !=は、ひとしくないという意味
// ""は文字が一つもない空っぽの文字列
// 合わせて、コンソール画面からユーザーによって入力された文字列が、空っぽではないという意味
if(Console.ReadLine() != "")
{
Console.WriteLine("ユーザーから入力がありました");
}
// 2回目
if(Console.ReadLine() != "")
{
Console.WriteLine("ユーザーから入力がありました");
}
// 3回目
if(Console.ReadLine() != "")
{
Console.WriteLine("ユーザーから入力がありました");
}
// 4回目
if(Console.ReadLine() != "")
{
Console.WriteLine("ユーザーから入力がありました");
}
// 以下エンドレス
残念ながら上のプログラムではうまくいきません。
ユーザーが何回入力するか分からないからです。
4回目までならうまくいきます。
でも、5回目以降は、入力されても何もできません。
逆に、ユーザーが4回目を入力せず辞めてしまったら、どうでしょう。
そしたら、プログラムはずっと止まったままです。
このように、『繰り返して何かを行う』ことそのものを、
何かの形でかけるようにしないと、
プログラムを書くのが大変になったり、
最悪書けなくなってしまいます。
(本日の内容一覧に戻る方はこちら)
ループ文がある世界
さあ、ここで、ループ文にご登場いただきましょう。
先ほどの二つの例を、ループ文で書き直しますね。
たった数行で同じことが書けますよ。
string[] mammals= {"dog", "cat", "pig", "monkey", "wolf", "deer", "sheep"};
foreach(var mammal in mammals)
{
Console.WriteLine(mammal);
}
while(Console.ReadLine() != "")
{
Console.WriteLine("ユーザーから入力がありました。");
}
では、ループ文の中身をもう少し見てみましょう。
ループ文は4種類
ループ文は4種類あります。それぞれ想定する使用目的が違うので、下のリストで概要をつかんでくださいね。
- 1. foreach文
- コンテナと組み合わせて使う
- 2. while文
- コンソール画面からの入力など、プログラム外の要素でループするか決めるときに使う
- 3. for文
-
一定の回数、ループを繰り返すときに使う
コンテナのインデックスを取得したいときに使う - 4. do-while文
- ある操作を行った後で、プログラム外の要素次第で継続するか決めるときに使う
foreach文
foreach文は配列やリストのようなコンテナと組み合わせるのが定石です。
コンテナの各要素を表示したい時や、コンテナの要素に合わせた条件分岐をする時に使います。
string[] personNames = {"Taro", "Hanako", "Jiro"};
foreach(var personName in personNames)
{
// 文字列の長さとは、文字列の中にある文字の個数です
// 例えば、"Hello"の中には、H, e, l, l, oの5文字があるので、長さは5です。
// もし(if)、personNameという文字列の長さ(Length)が5以上(>=)なら
if(personName.Length >= 5)
{
Console.WriteLine("5文字以上の名前です!");
}
else
{
Console.WriteLine("4文字以下の名前です。");
}
}
while文
while文は、条件を自由に指定できるループ文です。
ユーザーからの入力次第でループするか決める例をご紹介しましたね。
自由な条件を設定できる分、扱いが難しいですが、
一つの使い道は、
ユーザーとのやり取り(外部条件)で動きを変えるプログラムを作る時です。
while文には、代表的なフォーマットが2種類あります。
// フォーマット1. ループの継続を判断する真偽条件が1つしかなく、かつ簡単な時
// 真偽条件の初期設定(なくてもよい)
while(真偽条件判定
{
// 操作
// 真偽条件変更(次回の判定の結果を変更)
}
// フォーマット2.
// ループの継続を判断する真偽条件が2つ以上あるとき。
// あるいは条件は1つでも複雑で、while()の()の中に書くと読みにくい時
while(true)
{
// 真偽条件を判定する材料のデータ
var 真偽条件判定材料データの変数 = 真偽条件判定材料のデータ;
// ループ自体を終了する条件
if(ループ自体を終了する条件){break;}
// その回のループをスキップして、次の回のループに行く条件(不要ならなくてもよい)
if(その回のループをスキップする条件){continue;}
}
breakとcontinueについては後で詳しく述べます。
ここではこれだけ覚えてください。
breakは、ループ全体を終了させる。
continueは、その回のループはそこでスキップして次の回に行く。
条件はどちらもif()で指定する。
using System;
namespace WhileLoopSandBox
{
class MainClass
{
static void Main(string[] args)
{
Console.WriteLine("文字列の入力をして下さい");
// 10文字以下の入力を受け付けたい
var maxStringLength = 10;
// Console.ReadLine().Lengthは、コンソール画面(Console)が読み込んだ(ReadLine)文字列の長さ(Length)
while (Console.ReadLine().Length <= maxStringLength)
{
Console.WriteLine($"{maxStringLength}文字以下の入力です。続けてください。");
}
}
}
}
whileループの始まりから終わりまでをご覧いただきます。
using System;
namespace WhileLoopSandBox
{
class MainClass
{
static void Main(string[] args)
{
Console.WriteLine("文字列をご入力ください。");
// 条件が多い時のwhileループ
while (true)
{
// 条件の判定材料となるデータ
string currentUserInput = Console.ReadLine();
// ルーピング自体の停止条件
// 入力がないとルーピング終了
if (currentUserInput == "") { break; }
// 各ループターンのスキップ条件
// 11文字以上だとスキップして次のループへ
if (currentUserInput.Length >= 11) {
Console.WriteLine("長すぎです。やり直し。");
continue;
}
// ループが停止もせず、スキップもされなかった時の正常な動作
Console.WriteLine("きちんとご入力いただきありがとうございます。続けてください。");
}
}
}
}
for文
for文の基本フォーマットは以下の通りです。
直感的に分かりづらい構文なので、コメントで説明を入れます。
// ループのカウンターが、初期値から、終了値になるまでループ
// 『ループカウンター++』は、ループのカウンターをループが終わるごとに1つずつ増やしますという意味
for(var ループカウンター = 初期値; ループカウンター <= 終了値; ループカウンター++)
{
// ループの各回で行う処理;
}
for文の代表的な使い道から、2つを紹介します。
順番に見ていきましょう。
配列やリストの番号を取得して使う
配列やリストの番号と、番号に対応するデータを一度に表示したいとき、for文が使えます。
ループカウンターは、対象の配列やリストの個別データの番号を指します。それにふさわしい名前を付けましょう。
例えば、flowersという配列にfor文を使うなら、flowerIndexと名付けましょう。
ただし、対象の配列やリストが自明なら、短くindexでも構いません。
string[] flowers = {"tulip", "lily", "rose"};
// 配列やリストの番号(インデックス)を取得したいとき
// flowerIndexはflowersの個別のデータの番号
// flowerIndexは0始まりで、 flowersのデータ数( flowers.Length)より1つ少ない数で終わる
// ループが一つ進むごとに、 flowerIndexは1つ増える
for(var flowerIndex = 0; flowerIndex <= flowers.Length -1 ; flowerIndex++)
{
// 繰り返したい操作;
}
using System;
namespace ForStatementSandBox
{
class MainClass
{
static void Main(string[] args)
{
string[] fishes = {"carp", "sardine", "salmon"};
for(var fishIndex = 0; fishIndex <= fishes.Length - 1; fishIndex++)
{
Console.WriteLine($"{fishIndex}番目の魚 : {fishes[fishIndex]}");
}
}
}
}
指定した回数だけ何かの操作を繰り返す
指定した回数だけ、何かを繰り返すときもfor文は使います。
例えば、100回、コマンドライン画面に文字列を表示したい時です。
このような場合、ループカウンターの名前は、loopCountか、短くcountとしましょう。
// ループ終了回数がXで、X回繰り返したいとき
// ループのカウントは、1回目から、終了回数まで
for(var loopCount = 1; loopCount <= ループ終了回数X; loopCount++)
{
// 繰り返したい操作;
}
using System;
namespace ForStatementSandBox
{
class MainClass
{
static void Main(string[] args)
{
for(var loopCount = 1; loopCount <= 100; loopCount++)
{
Console.WriteLine($"{loopCount}回目の : for文ループ!");
}
}
}
}
do-while文
do-while文は、1回何かの操作を必ず実行します。
そののち、条件を満たせば、その操作を繰り返します。
条件を満たさない場合は、そのまま終了です。
上記の説明だけでは分かりにくいですね。フォーマットと実例を見ていただきましょう。
do
{
// 必ず1回はする操作
}
while(条件判定);
using System;
namespace DoWhileSandBox
{
class MainClass
{
static void Main(string[] args)
{
do
{
Console.WriteLine("10文字以下の文字列を入力しよう!");
}//, then
while (Console.ReadLine().Length <= 10);
// redo;
}
}
}
条件の真偽に関係なく、1回は{}の中身が実行されています。
1枚目では、最初に10文字以上入力したので、そこで終了です。
でも2枚目では、最初に10文字より短く入力したので、ループに入りました。
do-while文は、以下の構文が省略されたものと考えられます。
//まず操作1を必ずする
// 操作1
// その後、条件を満たしていれば、操作1を継続する
while(条件)
{
// 操作1
}
ループよ、さらば(break)
while文の説明で述べた通り、breakは『一連のループ自体にサヨナラする』です。
while文以外のループでも使えます。
例えば、for文やforeach文で、特別な事情があって、
既定の回数のループをしないまま、強制的にループを終了したいときです。
using System;
namespace BreakSandBox
{
class MainClass
{
static void Main(string[] args)
{
string[] salmonsWithOtherFish = { "salmon", "salmon", "salmon", "salmon", "carp", "salmon" };
foreach (var fish in salmonsWithOtherFish)
{
// ループを終了する条件
// もし一匹でもsalmonでないfishが見つかったら、そこでループは強制終了
if (fish != "salmon")
{
Console.WriteLine($"not salmon!{fish}!");
break;
}
// 何もなければこの操作まで至る
Console.WriteLine("salmon!");
}
}
}
}
次の回にスキップ(continue)
while文の説明で述べた通り、
continueは『そのループをスキップして、次のループに向かう』です。
while文以外のループでも使えます。
例えば、foreach文で、
配列やリストの中の各データの性質次第では、
処理をしないまま次のループにスキップするときに使います。
using System;
namespace ContinueSandBox
{
class MainClass
{
static void Main(string[] args)
{
string[] fishes = { "sardine", "carp", "salmon" };
foreach (var fish in fishes)
{
// salmonでなければ次のループにスキップ
if (fish != "salmon") {
Console.WriteLine("not salmon!");
continue;
}
// salmonであるときのみ、スキップされずこの操作に到達する
Console.WriteLine("salmon!");
}
}
}
}
ネストは浅く
ネストとは、入れ子構造のことです。
ループのネストは、できる限り一重、最悪でも二重までが望ましいでしょう。
三重以上のループでは、ループの管理が難しくなります。
皆さん自身の頭を悩ませてしまい、ミスを誘発します。
また、二重、三重ループでは、breakで簡単にループを抜け出す事はできません。
goto文を使い、
多重ループのすぐ真下に出るのが、最も現実的なやり方です。
しかし、goto文はチーム開発では原則禁止です。
なぜなら、ラベルさえつければ、プログラムのどこにでもワープできる危険すぎる構文だからです。
goto文のせいでハチャメチャなプログラムが簡単にできてしまいます。
goto文が現実的に使えない以上、ますます多重ループは避けるべきです。
using System;
using System.Linq;
namespace DeeplyNestedLoopingSandBox
{
class MainClass
{
static void Main(string[] args)
{
// 簡単な時刻表
int[] hours = Enumerable.Range(start: 0, count: 24).ToArray();
int[] minutes = Enumerable.Range(start: 0, count: 60).ToArray();
int[] seconds = Enumerable.Range(start: 0, count: 60).ToArray();
foreach (var hour in hours)
{
foreach (var minute in minutes)
{
foreach (var second in seconds)
{
var currentTime = $"{hour}:{minute}:{second}";
if (currentTime == "12:0:0")
{
Console.WriteLine($"もう{currentTime}! ループよ、さよなら!");
// 危険: goto文を使うしかなかった
// 三重ループなので仕方がない
goto ENTIRE_LOOP_END;
}
Console.WriteLine($"今は{hour}:{minute}:{second}");
}
}
}
// 三重ループの出口
ENTIRE_LOOP_END:
}
}
}
実習ループ文
問題1.全要素をコマンドライン画面に表示
1から1000000(百万)までの整数から作られた、
以下の配列の全要素を、コマンドライン画面に表示してください。
// 1から1000000までの整数がありますよ int[] integers_Min_1_Max_1000000 = Enumerable.Range(start: 1, count: 1000000).ToArray();
問題2. コマンドラインからの入力によるループ
ユーザーから、“yes”と入力され続ける限り、“loop!”とコンソール画面に表示するプログラムを作ってください。
問題3. breakとcontinueのコメントによる解説(初心者向け)
以下のコードでは、数字とローマンアルファベットの境目を見やすくするために、
例外的に『_』を複合語の句切れ目にしています。
using System;
namespace FizzBuzzSandBox
{
class MainClass
{
static void Main(string[] args)
{
while (true)
{
int randomInteger_Min_0_Max_100 = (new Random()).Next(start: 0, count: 101);
if (randomInteger_Min_0_Max_100 < 20) { break; }
if (randomInteger_Min_0_Max_100 > 90) { break; }
if (randomInteger_Min_0_Max_100 % 2 == 0) {
Console.WriteLine($"{randomInteger_Min_0_Max_100}は20以上90以下ですが偶数です。惜しい!");
continue;
}
Console.WriteLine($"乱数{randomInteger_Min_0_Max_100}は20以上90以下の奇数です。");
}
}
}
}
問題4. FizzBuzz
有名なFizzBuzz問題です。
1から100までの数について、15で割り切れるならFizzBuzzと表示してください。
15で割り切れない場合でも、3で割り切れればFizz、5で割り切れればBuzzと表示してください。
どれでも割り切れない場合は、数字そのものを表示してください。
問題5. 最初の入力次第でループするか決まるプログラム
5文字以下の文字列を入力するプログラムを書いてください。
必ず1回は実行され、5文字以下の文字列がその時に入力されたら、
引き続きループしてください。
まとめ
皆さんお疲れ様です。
これでこの連載の前半は終了です。
次回から、C#の肝、クラスについてご説明します。
クラスが使えるようになると、ますますできることが広がりますよ。
ゲーム制作関連のオススメ連載リンク
とっても手軽なゲーム制作体験!
Unityゲーム開発基礎
実際のリリースゲームを題材にしたハンズオンゲーム制作連載
実践unityゲーム開発









