C#サンプル問題に基礎から応用を適用してみる
サンプル問題
次の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()}");
いかがでしたか?それぞれの変更で、メリットを感じされましたか?
可読性(読みやすさ)を犠牲にすると本末転倒ですが、
ディスカッション
コメント一覧
まだ、コメントがありません