C#で 1〜10 をシャッフル表示― “まず動かす”→“メソッド化”→“正しいアルゴリズム”→“テストと VS 操作” まで一気に体験
目次
0. この記事で学ぶ流れ
ステップ | ゴール | キーワード |
---|---|---|
1 | Main だけ でとにかく動かす | 乱数・配列 |
2 | メソッド化 して読みやすく | 再利用・副作用防止 |
3 | Fisher–Yates で正しいシャッフル | 等確率・O(n) |
4 | NUnit + Assert.That で自動テスト | Constraint 構文 |
5 | Visual Studio でテストを実行 | Test Explorer |
途中で出てくる ToArray() のコピー理由や「参照/参照先」の正しい言い回しも詳細に取り上げます。
1. ステップ 1 ― Main だけで“動くもの”
using System;
using System.Linq;
namespace ShuffleSample
{
internal class Program
{
static void Main(string[] args)
{
int[] numbers = Enumerable.Range(1, 10).ToArray();
Random rnd = new Random();
for (int i = 0; i < numbers.Length; i++)
{
int j = rnd.Next(numbers.Length);
int temp = numbers[i];
numbers[i] = numbers[j];
numbers[j] = temp;
}
Console.WriteLine(string.Join(", ", numbers));
}
}
}
- まず動く:結果が出ることで安心感。
- 乱数生成をループ外で 1 回、以外は効率を意識しない。
2. ステップ 2 ― シャッフルをメソッドに切り出す
using System;
using System.Linq;
namespace ShuffleSample
{
internal class Program
{
static void Main(string[] args)
{
int[] source = Enumerable.Range(1, 10).ToArray();
int[] shuffled = Shuffle(source); // 呼び出すだけ
Console.WriteLine(string.Join(", ", shuffled));
}
/// <summary>要素順は雑でも良い“とりあえず”シャッフル</summary>
static int[] Shuffle(int[] source)
{
int[] result = source.ToArray(); // ★元配列を複製して守る
Random rnd = new Random();
for (int i = 0; i < result.Length; i++)
{
int j = rnd.Next(result.Length);
(result[i], result[j]) = (result[j], result[i]);
}
return result;
}
}
}
int[] result = source.ToArray(); が必要な理由
観点 | 説明 |
---|---|
参照型 | 配列は参照型。メソッドには“参照値”が値渡しされるため、呼び出し元と同じ 参照先(= 同一配列オブジェクト) を共有する。 |
副作用 | コピーを取らずに並べ替えると、呼び出し元の配列まで変更されてしまう。 |
ToArray() | LINQ 拡張。要素を同順で新しい int[] にコピーする。安全だが O(n) のコストとメモリ 2 倍が必要。 |
代替案 | var result = (int[])source.Clone(); や Array.Copy(…) でも可。目的は同じ。 |
3. ステップ 3 ― Fisher-Yates で“正しい”シャッフル
static int[] ShuffleFY(int[] source)
{
int[] result = source.ToArray(); // 参照先を複製
Random rnd = new Random();
for (int i = result.Length - 1; i > 0; i--)
{
int j = rnd.Next(i + 1); // 0〜i
(result[i], result[j]) = (result[j], result[i]);
}
return result;
}
- すべての順列が等確率。
- ループは n-1 回で O(n)、高速かつ偏りなし。
4. ステップ 4 ― NUnit + Assert.That で品質を守る
4-1. テスト コード
using NUnit.Framework;
using System.Linq;
namespace ShuffleSample.Tests
{
[TestFixture]
public class ShuffleTests
{
[Test]
public void ShuffleFY_UniquenessAndRange()
{
var src = Enumerable.Range(1, 10).ToArray();
var dst = Program.ShuffleFY(src);
Assert.That(dst, Has.Exactly(10).Items);
Assert.That(dst, Is.Unique.And.EquivalentTo(src));
}
}
}
4-2. Assert.That が優れるポイント
AreEqual 系 | That 系 (Constraint) |
---|---|
expected/actual が逆転しがち | actual → 条件 の自然な語順 |
メソッドが乱立 | Is, Has で 条件を組合せ |
簡素な失敗メッセージ | 条件ごとに詳細な差分表示 |
拡張しづらい | 独自 Constraint が実装容易 |
5. ステップ 5 ― Visual Studio で NUnit テストを実行
- ソリューションを右クリック → [追加] > [新しいプロジェクト]
- NUnit Test Project (.NET Core) を選択。
- テスト プロジェクト → [プロジェクト参照の追加] で本体プロジェクトにチェック。
- テスト クラスを作成し [TestFixture] / [Test] 属性を付ける。
- メニュー [テスト] > [Test Explorer] (Ctrl + E, T) を開く。
- Run All (Ctrl + R, A) で実行。
- 赤/緑アイコンで結果確認。右クリック [デバッグ] でブレークポイントも利用可。
ヒント: 歯車→ Run Tests After Build を ON にするとビルドごとに自動テスト。
6. まとめ ― 小さなリファクタリングを回す
- 動くものを最速で作り、振る舞いを確かめる。
- メソッド化 で読みやすさ・再利用性・副作用抑止。
- アルゴリズム改善 (Fisher-Yates) で正確さと性能を確保。
- テスト を書いて安全網を敷く。Assert.That で可読性◎。
- IDE 操作 を覚え、コード→テスト→デバッグをシームレスに。
この一連を体験すれば、「まず動かす」から「保守しやすいコード」へ成長するリファクタリングの筋肉が付きます。ぜひ手を動かし、赤 → 緑 のテストサイクルを回してみてください。
訪問数 30 回, 今日の訪問数 1回
ディスカッション
コメント一覧
まだ、コメントがありません