C# 基礎 第13回 回れ回れよグルグルと(ループ文)


回れ回れよグルグルと(ループ文)

今日は、同じことを何回も繰り返してもらいます。
でも大丈夫、皆さんがやるのは、
ほんの数行のプログラムを書くことだけです。
それだけで、何回も、何十回も、いや何万回も同じことが繰り返せます。
ループ文をマスターできれば!

本日の内容
  1. ループ文がない世界
  2. ループ文がある世界
  3. ループ文は4種類
  4. foreach文
  5. while文
  6. for文
  7. do-while文
  8. ループよ、さらば(break)
  9. 次の回にスキップ(continue)
  10. ネストは浅く
  11. 実習ループ文
  12. まとめ

ループ文がない世界

ループ文がない世界で、繰り返しのあるプログラムを書くにはどうすればよいでしょうか。
一言でいうと、大変、大変、大きな労苦を伴います。

試しに、ループ文のない世界で、配列の各要素をすべて表示するプログラムを書いてみましょう。

7匹の哺乳類の名前をすべてコンソール画面に表示する(ループのない世界)
// 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回目を入力せず辞めてしまったら、どうでしょう。
そしたら、プログラムはずっと止まったままです。

このように、『繰り返して何かを行う』ことそのものを、
何かの形でかけるようにしないと、
プログラムを書くのが大変になったり、
最悪書けなくなってしまいます。
(本日の内容一覧に戻る方はこちら)

ループ文がある世界

さあ、ここで、ループ文にご登場いただきましょう。
先ほどの二つの例を、ループ文で書き直しますね。
たった数行で同じことが書けますよ。

哺乳類の名前をすべてコンソール画面に表示する(foreach文
string[] mammals= {"dog", "cat", "pig", "monkey", "wolf", "deer", "sheep"};
foreach(var mammal in mammals)
{
    Console.WriteLine(mammal);
}
ユーザーの入力がある限りコンソール画面にメッセージを表示する(while文
while(Console.ReadLine() != "")
{
    Console.WriteLine("ユーザーから入力がありました。");
}

では、ループ文の中身をもう少し見てみましょう。

(本日の内容一覧に戻る方はこちら)

ループ文は4種類

ループ文は4種類あります。それぞれ想定する使用目的が違うので、下のリストで概要をつかんでくださいね。

ループ文の種類と使用目的
1. foreach文
コンテナと組み合わせて使う
2. while文
コンソール画面からの入力など、プログラム外の要素でループするか決めるときに使う
3. for文
一定の回数、ループを繰り返すときに使う
コンテナのインデックスを取得したいときに使う
4. do-while文
ある操作を行った後で、プログラム外の要素次第で継続するか決めるときに使う

(本日の内容一覧に戻る方はこちら)

foreach文

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種類あります。

while文のフォーマット2つ
// フォーマット1. ループの継続を判断する真偽条件が1つしかなく、かつ簡単な時
// 真偽条件の初期設定(なくてもよい)
while(真偽条件判定
{
    // 操作
    // 真偽条件変更(次回の判定の結果を変更)
}

// フォーマット2.
// ループの継続を判断する真偽条件が2つ以上あるとき。
// あるいは条件は1つでも複雑で、while()の()の中に書くと読みにくい時

while(true)
{
    // 真偽条件を判定する材料のデータ
    var 真偽条件判定材料データの変数 = 真偽条件判定材料のデータ;
    // ループ自体を終了する条件
    if(ループ自体を終了する条件){break;}
    // その回のループをスキップして、次の回のループに行く条件(不要ならなくてもよい)
    if(その回のループをスキップする条件){continue;}
}

breakとcontinueについては後で詳しく述べます。
ここではこれだけ覚えてください。

breakは、ループ全体を終了させる

continueは、その回のループはそこでスキップして次の回に行く

条件はどちらもif()で指定する。

フォーマット1の実際のコード
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}文字以下の入力です。続けてください。");
            }
        }
    }
}
フォーマット1のコードの実行結果

whileループの始まりから終わりまでをご覧いただきます。

ループスタート

最初のループが成功して2回目のループへ

ループ継続中

継続する条件が満たされなかったのでループ終了

フォーマット2の実際のコード
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("きちんとご入力いただきありがとうございます。続けてください。");
            }
        }
    }
}
フォーマット2のコードの実行結果

(本日の内容一覧に戻る方はこちら)

for文

for文の基本フォーマットは以下の通りです。
直感的に分かりづらい構文なので、コメントで説明を入れます。

for文の基本的なフォーマット
// ループのカウンターが、初期値から、終了値になるまでループ
// 『ループカウンター++』は、ループのカウンターをループが終わるごとに1つずつ増やしますという意味
for(var ループカウンター = 初期値; ループカウンター <= 終了値; ループカウンター++)
{
    // ループの各回で行う処理;
}

for文の代表的な使い道から、2つを紹介します。

for文の2つの使い方
  1. 配列やリストの番号を取得して使う
  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文の2つの使い方リストに戻る方はこちら)

指定した回数だけ何かの操作を繰り返す

指定した回数だけ、何かを繰り返すときも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文ループ!");
            }
        }
    }
}
実行結果

(for文の2つの使い方リストに戻る方はこちら)

コラム: 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文字より短く入力したので、ループに入りました。

1枚目: 絶対に1回は実行される

2枚目: 何回も実行されている

do-while文の起源

do-while文は、以下の構文が省略されたものと考えられます。

//まず操作1を必ずする
    // 操作1

// その後、条件を満たしていれば、操作1を継続する
while(条件)
{
    // 操作1
}
コラム: do-while文不要論って?

(本日の内容一覧に戻る方はこちら)

ループよ、さらば(break)

while文の説明で述べた通り、breakは『一連のループ自体にサヨナラする』です。

while文以外のループでも使えます。
例えば、for文やforeach文で、特別な事情があって、
既定の回数のループをしないまま、強制的にループを終了したいときです。

breakの例
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文で、
配列やリストの中の各データの性質次第では、
処理をしないまま次のループにスキップするときに使います。

continueの例
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文が現実的に使えない以上、ますます多重ループは避けるべきです。

3重ループ実例
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と表示してください。

どれでも割り切れない場合は、数字そのものを表示してください。

解答例 for文を使って

問題5. 最初の入力次第でループするか決まるプログラム

5文字以下の文字列を入力するプログラムを書いてください。
必ず1回は実行され、5文字以下の文字列がその時に入力されたら、
引き続きループしてください。

解答例 do-while文を使って

(本日の内容一覧に戻る方はこちら)

まとめ

皆さんお疲れ様です。
これでこの連載の前半は終了です。
次回から、C#の肝、クラスについてご説明します。
クラスが使えるようになると、ますますできることが広がりますよ。

(本日の内容一覧に戻る方はこちら)

(最初から読み直したい方はこちら)

  • このエントリーをはてなブックマークに追加

PAGE TOP