C#サンプル問題に基礎から応用を適用してみる

2023年1月27日

サンプル問題

次の5つの整数で、最大(MAX)のもの、最小(MIN)のもの、また平均値を表示するプログラムを作りましょう。

4 5 2 7 1

表示結果サンプル

DATA = 4 5 2 7 1
最大(MAX) = 7
最小(MIN) = 1
平均値(AVERAGE) = 3.8

解答方法1(変数とif文)

元になる変数

宣言

まず、プログラムで使う変数を整数型で宣言しましょう

nt num1;
int num2;
int num3;
int num4;
int num5;

代入

次にそれぞれの変数に整数値を代入します。

num1 = 4;
num2 = 5;
num3 = 2;
num4 = 7;
num5 = 1;

データを一覧表示する

Console.WriteLine($"DATA = {num1} {num2} {num3} {num4} {num5}");

最大値の計算

最大値を入れておく変数の宣言の宣言

// 最大値を入れておく変数
int max;

初期値0の代入

// 最初に0を代入しておく
max = 0;

計算式

もし、1つ目の数がmaxより大きかったらmaxを置き換える
最初はmaxが0でnum1が4なので、maxは4になる

if (num1 > max)
{
    max = num1;
}

他のデータについても同様に繰り返す

if (num1 > max)
{
    max = num1;
}
if (num2 > max)
{
    max = num2;
}
if (num3 > max)
{
    max = num3;
}
if (num4 > max)
{
    max = num4;
}
if (num5 > max)
{
    max = num5;
}

最小値の計算

最小値を入れておく変数の宣言の宣言

// 最小値を入れておく変数
int min;

初期値1000(十分大きな値として)の代入

// 最初に1000を代入しておく
min = 0;

計算式

もし、1つ目の数がminより小さかったらminを置き換える
最初はminが1000でnum1が4なので、minは4になる

if (num1 < min)
{
    min = num1;
}

他のデータについても同様に繰り返す

if (num1 < min)
{
min = num1;
}
if (num2 < min)
{
    min = num2;
}
if (num3 < min)
{
    min = num3;
}
if (num4 < min)
{
    min = num4;
}
if (num5 < min)
{
    min = num5;
}

結果を表示する

Console.WriteLine($"最小(MIN) = {min}");

平均値の計算

平均値を入れておく変数の宣言の宣言

// 最小値を入れておく変数
float average;

計算式

全ての値を足し算して要素の数で割ります。

average = sum / 5.0f;

結果を表示する

Console.WriteLine($"平均値(AVERAGE) = {average}");

ここまでの表示結果

結果は期待通りになりました。

DATA = 4 5 2 7 1
最大(MAX) = 7
最小(MIN) = 1
平均値(AVERAGE) = 3.8

コード一覧

動作はしていますが、残念な点があります

  • 元のデータを増やすと多くの変更箇所が発生する
  • 元のデータを減らしても同じく多くの変更箇所が発生する
  • Mainメソッドがすでに100行近くになり、見づらい

これを100個の数字で計算してくださいと言われると、もうお手上げですね。

using System;
namespace BasicToevelopment
{
    class Program
    {
        static void Main(string[] args)
        {
            int num1;
            int num2;
            int num3;
            int num4;
            int num5;
            num1 = 4;
            num2 = 5;
            num3 = 2;
            num4 = 7;
            num5 = 1;
            Console.WriteLine($"DATA = {num1} {num2} {num3} {num4} {num5}");
            int max;
            max = 0;
            if (num1 > max)
            {
                max = num1;
            }
            if (num2 > max)
            {
                max = num2;
            }
            if (num3 > max)
            {
                max = num3;
            }
            if (num4 > max)
            {
                max = num4;
            }
            if (num5 > max)
            {
                max = num5;
            }
            Console.WriteLine($"最大(MAX) = {max}");
            int min;
            min = 1000;
            if (num1 < min)
            {
                min = num1;
            }
            if (num2 < min)
            {
                min = num2;
            }
            if (num3 < min)
            {
                min = num3;
            }
            if (num4 < min)
            {
                min = num4;
            }
            if (num5 < min)
            {
                min = num5;
            }
            Console.WriteLine($"最小(MIN) = {min}");
            int sum = num1 + num2 + num3 + num4 + num5;
            float average;
            average = sum / 5.0f;
            Console.WriteLine($"平均値(AVERAGE) = {average}");
        }
    }
}

コード一覧(リファクタリング後)

変数の宣言と代入を1行にまとめましょう。
コードの見易さは、損なわれずに10行ほど短くなりました。
ただ、まだ要素数の増減については対応できていないです。

using System;
namespace BasicToevelopment
{
    class Program
    {
        static void Main(string[] args)
        {
            int num1 = 4;
            int num2 = 5;
            int num3 = 2;
            int num4 = 7;
            int num5 = 1;
            Console.WriteLine($"DATA = {num1} {num2} {num3} {num4} {num5}");
            int max = 0;
            if (num1 > max)
            {
                max = num1;
            }
            if (num2 > max)
            {
                max = num2;
            }
            if (num3 > max)
            {
                max = num3;
            }
            if (num4 > max)
            {
                max = num4;
            }
            if (num5 > max)
            {
                max = num5;
            }
            Console.WriteLine($"最大(MAX) = {max}");
            int min = 1000;
            if (num1 < min)
            {
                min = num1;
            }
            if (num2 < min)
            {
                min = num2;
            }
            if (num3 < min)
            {
                min = num3;
            }
            if (num4 < min)
            {
                min = num4;
            }
            if (num5 < min)
            {
                min = num5;
            }
            Console.WriteLine($"最小(MIN) = {min}");
            int sum = num1 + num2 + num3 + num4 + num5;
            float average = sum / 5.0f;
            Console.WriteLine($"平均値(AVERAGE) = {average}");
        }
    }
}

解答方法2(配列)

単純に置き換えてみる

using System;
namespace BasicToevelopment
{
    class Program
    {
        static void Main(string[] args)
        {
            int[] nums = new int[5];
            nums[0] = 4;
            nums[1] = 5;
            nums[2] = 2;
            nums[3] = 7;
            nums[4] = 1;
            Console.WriteLine($"DATA = {nums[0]} {nums[1]} {nums[2]} {nums[3]} {nums[4]}");
            int max = 0;
            if (nums[0] > max)
            {
                max = nums[0];
            }
            if (nums[1] > max)
            {
                max = nums[1];
            }
            if (nums[2] > max)
            {
                max = nums[2];
            }
            if (nums[3] > max)
            {
                max = nums[3];
            }
            if (nums[4] > max)
            {
                max = nums[4];
            }
            Console.WriteLine($"最大(MAX) = {max}");
            int min = 1000;
            if (nums[0] < min)
            {
                min = nums[0];
            }
            if (nums[1] < min)
            {
                min = nums[1];
            }
            if (nums[2] < min)
            {
                min = nums[2];
            }
            if (nums[3] < min)
            {
                min = nums[3];
            }
            if (nums[4] < min)
            {
                min = nums[4];
            }
            Console.WriteLine($"最小(MIN) = {min}");
            int sum = nums[0] + nums[1] + nums[2] + nums[3] + nums[4];
            float average = sum / 5.0f;
            Console.WriteLine($"平均値(AVERAGE) = {average}");
        }
    }
}

最大値の計算が繰り返しになっているので、繰り返し文に置き換えてみる(For文)

using System;
namespace BasicToevelopment
{
    class Program
    {
        static void Main(string[] args)
        {
            int[] nums = new int[5];
            nums[0] = 4;
            nums[1] = 5;
            nums[2] = 2;
            nums[3] = 7;
            nums[4] = 1;
            Console.WriteLine($"DATA = {nums[0]} {nums[1]} {nums[2]} {nums[3]} {nums[4]}");
            int max = 0;
            for (int i = 0; i < 4; i++)
            {
                if (nums[i] > max)
                {
                    max = nums[i];
                }
            }
            Console.WriteLine($"最大(MAX) = {max}");
            int min = 1000;
            if (nums[0] < min)
            {
                min = nums[0];
            }
            if (nums[1] < min)
            {
                min = nums[1];
            }
            if (nums[2] < min)
            {
                min = nums[2];
            }
            if (nums[3] < min)
            {
                min = nums[3];
            }
            if (nums[4] < min)
            {
                min = nums[4];
            }
            Console.WriteLine($"最小(MIN) = {min}");
            int sum = nums[0] + nums[1] + nums[2] + nums[3] + nums[4];
            float average = sum / 5.0f;
            Console.WriteLine($"平均値(AVERAGE) = {average}");
        }
    }
}

続けて最小値も置き換えてみる(For文)

using System;
namespace BasicToevelopment
{
    class Program
    {
        static void Main(string[] args)
        {
            int[] nums = new int[5];
            nums[0] = 4;
            nums[1] = 5;
            nums[2] = 2;
            nums[3] = 7;
            nums[4] = 1;
            Console.WriteLine($"DATA = {nums[0]} {nums[1]} {nums[2]} {nums[3]} {nums[4]}");
            int max = 0;
            for (int i = 0; i < 4; i++)
            {
                if (nums[i] > max)
                {
                    max = nums[i];
                }
            }
            Console.WriteLine($"最大(MAX) = {max}");
            int min = 1000;
            for (int i = 0; i < 4; i++)
            {
                if (nums[i] < min)
                {
                    min = nums[i];
                }
            }
            Console.WriteLine($"最小(MIN) = {min}");
            int sum = nums[0] + nums[1] + nums[2] + nums[3] + nums[4];
            float average = sum / 5.0f;
            Console.WriteLine($"平均値(AVERAGE) = {average}");
        }
    }
}

配列の初期値代入を省略化してみる

一気にコード行が減りました。(50行ほど削減されています。)
ずいぶんすっきりしてきました。

using System;
namespace BasicToevelopment
{
    class Program
    {
        static void Main(string[] args)
        {
            int[] nums = { 4, 5, 2, 7, 1 };
            Console.WriteLine($"DATA = {nums[0]} {nums[1]} {nums[2]} {nums[3]} {nums[4]}");
            int max = 0;
            for (int i = 0; i < 4; i++)
            {
                if (nums[i] > max)
                {
                    max = nums[i];
                }
            }
            Console.WriteLine($"最大(MAX) = {max}");
            int min = 1000;
            for (int i = 0; i < 4; i++)
            {
                if (nums[i] < min)
                {
                    min = nums[i];
                }
            }
            Console.WriteLine($"最小(MIN) = {min}");
            int sum = nums[0] + nums[1] + nums[2] + nums[3] + nums[4];
            float average = sum / 5.0f;
            Console.WriteLine($"平均値(AVERAGE) = {average}");
        }
    }
}

要素の数が変更されても対応できるようにする

これまでのコードでは、要素数は固定でした。要素数は、要素数.Lengthで取得できるのでこれに置き換えます。また、最初のデータの表示部分もFor文に置き換えておきます。足し算をするコードもFor文で対応します。

using System;
namespace BasicToevelopment
{
    class Program
    {
        static void Main(string[] args)
        {
            int[] nums = { 4, 5, 2, 7, 1, 10 };
            Console.Write($"DATA = ");
            for (int i = 0; i < nums.Length; i++)
            {
                Console.Write($"{nums[i]} ");
            }
            Console.WriteLine();
            int max = 0;
            for (int i = 0; i < nums.Length; i++)
            {
                if (nums[i] > max)
                {
                    max = nums[i];
                }
            }
            Console.WriteLine($"最大(MAX) = {max}");
            int min = 1000;
            for (int i = 0; i < nums.Length; i++)
            {
                if (nums[i] < min)
                {
                    min = nums[i];
                }
            }
            Console.WriteLine($"最小(MIN) = {min}");
            int sum = 0;
            for (int i = 0; i < nums.Length; i++)
            {
                sum += nums[i];
            }
            float average = (float)sum / nums.Length;
            Console.WriteLine($"平均値(AVERAGE) = {average}");
        }
    }
}

データを置き換えて確認してみましょう

int[] nums = { 4, 5, 2, 7, 1, 10 };

結果

DATA = 4 5 2 7 1 10 
最大(MAX) = 10
最小(MIN) = 1
平均値(AVERAGE) = 4.833333

メソッドにする

最大値を求めるコードをメソッド化します。
次の部分になります。

int max = 0;
for (int i = 0; i < nums.Length; i++)
{
    if (nums[i] > max)
    {
        max = nums[i];
    }
}

VisualStudioのインテリセンスで自動的に作成できます。

作成後

Mainメソッド内

int max = Max(nums);

作られたメソッド

static int Max(int[] nums)
{
    int max = 0;
    for (int i = 0; i < nums.Length; i++)
    {
        if (nums[i] > max)
        {
            max = nums[i];
        }
    }
    return max;
}

Mainメソッドから、Maxメソッドにデータ配列を渡して、結果をint型で返してもらいます。

Minメソッド、Averageメソッドも同様に作成します。

static int Min(int[] nums)
{
    int min = 1000;
    for (int i = 0; i < nums.Length; i++)
    {
        if (nums[i] < min)
        {
            min = nums[i];
        }
    }
    return min;
}
static float Average(int[] nums)
{
    int sum = 0;
    for (int i = 0; i < nums.Length; i++)
    {
        sum += nums[i];
    }
    float average = (float)sum / nums.Length;
    return average;
}

メインメソッドは次のようになります。

static void Main(string[] args)
{
    int[] nums = { 4, 5, 2, 7, 1, 10 };
    Console.Write($"DATA = ");
    for (int i = 0; i < nums.Length; i++)
    {
        Console.Write($"{nums[i]} ");
    }
    Console.WriteLine();
    int max = Max(nums);
    Console.WriteLine($"最大(MAX) = {max}");
    int min = Min(nums);
    Console.WriteLine($"最小(MIN) = {min}");
    float average = Average(nums);
    Console.WriteLine($"平均値(AVERAGE) = {average}");
}

LINQを使う

MaxメソッドをLINQを使って置き換えます。

static int Max(int[] nums)
{
    return nums.Max();
}

Minメソッド、Averageメソッドも置き換えます。

static int Min(int[] nums)
{
    return  nums.Min();
}
static float Average(int[] nums)
{
    return (float)nums.Average();
}

タプルを使う

3つのメソッドをまとめて1つのメソッドにします。戻り値を複数返せるC#7から使えるタプルで作成します。

static (int max,int min,float average) MaxMixAverage(int[] nums)
{
    return (nums.Max(), nums.Min(), (float)nums.Average());
}

Mainメソッドは次のように変更します。

(int max, int min, float average) = MaxMixAverage(nums);

さらに、メソッドから戻り値の型が推論できますので、

var (max, min, average) = MaxMixAverage(nums);

以上をまとめると、

using System;
using System.Linq;
namespace BasicToevelopment
{
    class Program
    {
        static void Main(string[] args)
        {
            int[] nums = { 4, 5, 2, 7, 1, 10 };
            Console.Write($"DATA = ");
            for (int i = 0; i < nums.Length; i++)
            {
                Console.Write($"{nums[i]} ");
            }
            Console.WriteLine();
            var (max, min, average) = MaxMixAverage(nums);
            Console.WriteLine($"最大(MAX) = {max}");
            Console.WriteLine($"最小(MIN) = {min}");
            Console.WriteLine($"平均値(AVERAGE) = {average}");
        }
        static (int max, int min, float average) MaxMixAverage(int[] nums)
        {
            return (nums.Max(), nums.Min(), (float)nums.Average());
        }
    }
}

メソッドを式本体にする

ここまでで十分ですが、さらにfor文を消して、メソッドを式本体にすると

using System;
using System.Linq;
namespace BasicToevelopment
{
    class Program
    {
        static void Main(string[] args)
        {
            int[] nums = { 4, 5, 2, 7, 1, 10 };
            Console.Write($"DATA = ");
            nums.ToList().ForEach(n => Console.Write($"{n} "));
            Console.WriteLine();
            var (max, min, average) = MaxMixAverage(nums);
            Console.WriteLine($"最大(MAX) = {max}");
            Console.WriteLine($"最小(MIN) = {min}");
            Console.WriteLine($"平均値(AVERAGE) = {average}");
        }
        static (int max, int min, float average) MaxMixAverage(int[] nums)
            => (nums.Max(), nums.Min(), (float)nums.Average());
    }
}

処理結果は最初と同じです。コード行は1/3になりました。改行を除くと14行まで減っています。

さらにさらに、やりすぎを恐れずに書くと、Mainメソッドは4行まで減らすこともできます。

using System.Linq;
using static System.Console;
namespace BasicToevelopment
{
    class Program
    {
        static void Main()
        {
            int[] nums = { 4, 5, 2, 7, 1, 10 };
            Write($"DATA = ");
            nums.ToList().ForEach(n => Write($"{n} "));
            Show(MaxMixAverage(nums));
        }
        static (int max, int min, float average) MaxMixAverage(int[] nums)
            => (nums.Max(), nums.Min(), (float)nums.Average());
        static void Show((int max, int min, float average) calc)
        {
            WriteLine();
            WriteLine($"最大(MAX) = {calc.max}");
            WriteLine($"最小(MIN) = {calc.min}");
            WriteLine($"平均値(AVERAGE) = {calc.average}");
        }
    }
}

IEnumerable型に変更

List<> 型のデータでも使えるようにするため、配列、List<>両方が実装しているIEnumerable<> を引数にとるメソッドを追加します。

using System.Collections.Generic;
using System.Linq;
using static System.Console;
namespace BasicToevelopment
{
    class Program
    {
        static void Main()
        {
            IEnumerable<int> nums;
            nums = new[] { 4, 5, 2, 7, 1, 10 };
            Run(nums);
            nums = new List<int> { 4, 5, 2, 7, 1, 10 };
            Run(nums);
        }
        private static void Run(IEnumerable<int> nums)
        {
            Write($"DATA = ");
            nums.ToList().ForEach(n => Write($"{n} "));
            Show(MaxMixAverage(nums));
        }
        static (int max, int min, float average) MaxMixAverage(IEnumerable<int> nums)
            => (nums.Max(), nums.Min(), (float)nums.Average());
        static void Show((int max, int min, float average) calc)
        {
            WriteLine();
            WriteLine($"最大(MAX) = {calc.max}");
            WriteLine($"最小(MIN) = {calc.min}");
            WriteLine($"平均値(AVERAGE) = {calc.average}");
        }
    }
}

Dictionary型も追加してみると

using System.Collections.Generic;
using System.Linq;
using static System.Console;
namespace BasicToevelopment
{
    class Program
    {
        static void Main()
        {
            IEnumerable<int> nums;
            nums = new int[] { 4, 5, 2, 7, 1, 10 };
            Run(nums);
            nums = new List<int> { 4, 5, 2, 7, 1, 10 };
            Run(nums);
            nums = new Dictionary<int, int> { [2] = 4, [3] = 5, [6] = 2, [9] = 7, [1] = 1, [4] = 10 }.Values.ToList();
            Run(nums);
        }
        private static void Run(IEnumerable<int> nums)
        {
            Write($"DATA = ");
            nums.ToList().ForEach(n => Write($"{n} "));
            Show(MaxMixAverage(nums));
        }
        static (int max, int min, float average) MaxMixAverage(IEnumerable<int> nums)
            => (nums.Max(), nums.Min(), (float)nums.Average());
        static void Show((int max, int min, float average) calc)
        {
            WriteLine();
            WriteLine($"最大(MAX) = {calc.max}");
            WriteLine($"最小(MIN) = {calc.min}");
            WriteLine($"平均値(AVERAGE) = {calc.average}");
        }
    }
}

引数に直接インスタンスを渡す(new)こともできます。

using System.Collections.Generic;
using System.Linq;
using static System.Console;
namespace BasicToevelopment
{
    class Program
    {
        static void Main()
        {
            Run(new[] { 4, 5, 2, 7, 1, 10 });
            Run(new List<int> { 4, 5, 2, 7, 1, 10 });
            Run(new Dictionary<int, int> { [2] = 4, [3] = 5, [6] = 2, [9] = 7, [1] = 1, [4] = 10 }.Values.ToList());
        }
        private static void Run(IEnumerable<int> nums)
        {
            Write($"DATA = ");
            nums.ToList().ForEach(n => Write($"{n} "));
            Show(MaxMixAverage(nums));
        }
        static (int max, int min, float average) MaxMixAverage(IEnumerable<int> nums)
            => (nums.Max(), nums.Min(), (float)nums.Average());
        static void Show((int max, int min, float average) calc)
        {
            WriteLine();
            WriteLine($"最大(MAX) = {calc.max}");
            WriteLine($"最小(MIN) = {calc.min}");
            WriteLine($"平均値(AVERAGE) = {calc.average}");
        }
    }
}

コレクションの結合テスト

using System.Collections.Generic;
using System.Linq;
using static System.Console;
namespace BasicToevelopment
{
    class Program
    {
        static void Main()
        {
            IEnumerable<int> nums1, nums2, nums3;
            nums1 = new int[] { 4, 5, 2, 7, 1, 10 };
            nums2 = new List<int> { 4, 5, 2, 7, 1, 10 };
            nums3 = new Dictionary<int, int> { [2] = 4, [3] = 5, [6] = 2, [9] = 7, [1] = 1, [4] = 10 }.Values.ToList();
            Run(nums1.Concat(nums2).Concat(nums3));
        }
        static void Run(IEnumerable<int> nums)
        {
            Write($"DATA = ");
            nums.ToList().ForEach(n => Write($"{n} "));
            Show(MaxMixAverage(nums));
        }
        static (int max, int min, float average) MaxMixAverage(IEnumerable<int> nums)
            => (nums.Max(), nums.Min(), (float)nums.Average());
        static void Show((int max, int min, float average) calc)
        {
            WriteLine();
            WriteLine($"最大(MAX) = {calc.max}");
            WriteLine($"最小(MIN) = {calc.min}");
            WriteLine($"平均値(AVERAGE) = {calc.average}");
        }
    }
}

汎用性を少し残しつつ、結果だけ求めると

using System.Collections.Generic;
using System.Linq;
using static System.Console;
namespace BasicToevelopment
{
    class Program
    {
        static void Main()
        {
            IEnumerable<int> nums1, nums2, nums3;
            nums1 = new int[] { 4, 5 };
            nums2 = new List<int> { 2, 7 };
            nums3 = new Dictionary<int, int> { [1] = 1, [4] = 10 }.Values.ToList();
            var num = nums1.Concat(nums2).Concat(nums3);
            Write($"DATA = ");
            num.ToList().ForEach(n => Write($"{n} "));
            WriteLine($"\n最大(MAX) = {num.Max()}\n最小(MIN) = {num.Min()}\n平均値(AVERAGE) = {num.Average()}");
        }
    }
}

若干のフォーマットを残しつつ、結果だけ表示するコード

.NET6以前の書き方(.NET6でも書けますが・・)

using System.Collections.Generic;
using System.Linq;
using static System.Console;
namespace BasicToevelopment
{
    class Program
    {
        static void Main()
        {
            var num = new int[] { 4, 5, 2, 7, 1, 10 };
            Write($"DATA = ");
            num.ToList().ForEach(n => Write($"{n} "));
            WriteLine($"\n最大(MAX) = {num.Max()}\n最小(MIN) = {num.Min()}\n平均値(AVERAGE) = {num.Average()}");
        }
    }
}

.NET6以降の書き方

トップレベルステートメントといいます
日本語では、最上位レベルステートメント
ProgramクラスやMainメソッドのコードをC#側で代わりに裏で書いてくれる機能です

using static System.Console;

var num = new int[] { 4, 5, 2, 7, 1, 10 };
Write($"DATA = ");
num.ToList().ForEach(n => Write($"{n} "));
WriteLine($"\n最大(MAX) = {num.Max()}\n最小(MIN) = {num.Min()}\n平均値(AVERAGE) = {num.Average()}");

機能確認だけのコード

ただ単に確認のため結果だけ欲しい場合、これだけでもいいですね

var num = new int[] { 4, 5, 2, 7, 1, 10 };
WriteLine($"{num.Max()}:{num.Min()}:{num.Average()}");

いかがでしたか?それぞれの変更で、メリットを感じされましたか?
可読性(読みやすさ)を犠牲にすると本末転倒ですが、

C#

Posted by hidepon