ナベアツネタをプログラミングしてみる
皆様こんにちは。
最近エルガド警備員からパルデア警備員に転職した下位です。
コライドンが可愛すぎて日々悶絶しております。
なんだあのキュートな生物は
さて、本日は昔懐かしナベアツネタをプログラムに起こします。
3 のつく数字と 3 の倍数でお上品になるあれです。
基本の型
まずは基本の型です。
難しいことは考えずに、さくっと作ってしまいましょう。
お上品ケースは ★ で装飾をつけています。
class Program {
static void Main(string[] args) {
var list = Enumerable.Range(1, 30).ToList();
list.ForEach(d =>
{
if (IsMetamorphoseOjyohin(d))
{
Console.Write($"★{d}★,");
} else
{
Console.Write($"{d},");
}
});
}
/**
* お上品になる条件の判定
**/
static bool IsMetamorphoseOjyohin(int num)
{
if (num % 3 == 0) return true;
return num.ToString().Contains("3");
}
}
出力ログです。
検証はしてないですが、良さげですね。
1,2,★3★,4,5,★6★,7,8,★9★,10,11,★12★,★13★,14,★15★,16,17,★18★,19,20,★21★,22,★23★,★24★,25,26,★27★,28,29,★30★,
応用の型
さて、基本の型だけで終わるとあまりにも味気ない記事になりますね。
これでは管理部署からお叱り愛の鞭を頂いてしまいそうなので、もう少し仕様を加えてみます。
大体こんな感じです。
- 起動モードを付加する(0=通常, 1=お上品, 2=カウント反転 + お上品)
- お上品モードのときは数値を ★ でラップする
ということで、仕様変更版です。
class Program
{
static readonly ReadOnlyCollection<string> PROC_TYPES = new List<string>() { "0", "1", "2" }.AsReadOnly();
static void Main(string[] args)
{
if (!args.Any() || args.Length > 1)
{
throw new ArgumentException($"起動モード未設定または複数の設定:{args.Length}");
}
string procType = args.First();
if (!PROC_TYPES.Contains(procType)) {
throw new ArgumentException($"不正な起動タイプ:{procType}");
}
var list = Enumerable.Range(1, 30).ToList();
// 反転 + お上品
if (procType == "2")
{
list.Reverse();
list.ForEach(d =>
{
string fmt = IsMetamorphoseOjyohin(d) ? $"★{d}★," : $"{d},";
Console.Write(fmt);
});
return;
}
// お上品
if (procType == "1")
{
list.ForEach(d =>
{
string fmt = IsMetamorphoseOjyohin(d) ? $"★{d}★," : $"{d},";
Console.Write(fmt);
});
return;
}
// ノーマル
list.ForEach(d =>
{
Console.Write($"{d},");
});
}
/**
* お上品になる条件の判定
**/
static bool IsMetamorphoseOjyohin(int num) => num % 3 == 0 || num.ToString().Contains("3");
}
ログです。
引数に対して良き感じで装飾がついてますね。
通常、お上品ケース共に申し分ありません。
$ dotnet run 0
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,
$ dotnet run 1
1,2,★3★,4,5,★6★,7,8,★9★,10,11,★12★,★13★,14,★15★,16,17,★18★,19,20,★21★,22,★23★,★24★,25,26,★27★,28,29,★30★,
$ dotnet run 2
★30★,29,28,★27★,26,25,★24★,★23★,22,★21★,20,19,★18★,17,16,★15★,14,★13★,★12★,11,10,★9★,8,7,★6★,5,4,★3★,2,1,
じゃぁリファクタリングしましょうか。
上記コードなんて提出したら、ソースレビューでボコボコにされるリテイク要求されること間違いなしです。
リファクタリング編
仕様を満たすプログラムを作れたならリファクタリングです。
えれがんとなこーど(自称) を作る為、極限まで無駄を省きましょう。
えれがんとなこーどが地球を救うことはないですが、同業種のエンジニアは救ってくれます。
(某 24 時間 TV にこんな標語がありましたね。懐かしい)
では個人的に駄目なポイント解説です。コメントふります。
class Program
{
// ①
static readonly ReadOnlyCollection<string> PROC_TYPES = new List<string>() { "0", "1", "2" }.AsReadOnly();
static void Main(string[] args)
{
if (!args.Any() || args.Length > 1)
{
throw new ArgumentException($"起動モード未設定または複数の設定:{args.Length}");
}
string procType = args.First();
if (!PROC_TYPES.Contains(procType)) {
throw new ArgumentException($"不正な起動タイプ:{procType}");
}
var list = Enumerable.Range(1, 30).ToList();
// ①
if (procType == "2")
{
list.Reverse();
// ②
list.ForEach(d =>
{
string fmt = IsMetamorphoseOjyohin(d) ? $"★{d}★," : $"{d},";
Console.Write(fmt);
});
return;
}
// ①
if (procType == "1")
{
// ②
list.ForEach(d =>
{
string fmt = IsMetamorphoseOjyohin(d) ? $"★{d}★," : $"{d},";
Console.Write(fmt);
});
return;
}
// ①,②
list.ForEach(d =>
{
Console.Write($"{d},");
});
}
/**
* お上品になる条件の判定
**/
static bool IsMetamorphoseOjyohin(int num) => num % 3 == 0 || num.ToString().Contains("3");
}
① 起動タイプを文字列管理
受け入れるべき値がわかっているのであれば、Enum です。
定数管理もありですが、グループ化できる定数なら Enum 使うべきです。
あとリテラル比較は絶対 NG です。記述の背景がわからなくなるので、後任エンジニアが宇宙猫(※)になります。
※気になる人は Google 検索です。
② 重複処理が多すぎる
同じような処理に散見されますね。コピペコードは技術的負債になる可能性が高いです。纏めましょう。
改善版コード
前述のポイントを改善したものがこちら。
リテラルを Enum に置換するだけでもコードの見通しは変わるものです。
namespace Sample {
enum ProcType {
NORMAL,
ELEGANT,
ELEGANT_REVERSE
}
class Program
{
static void Main(string[] args)
{
if (!args.Any() || args.Count() > 1)
{
throw new ArgumentException($"起動モード未設定または複数の設定:{args.Length}");
}
if (!(Enum.TryParse(args.First(), false, out ProcType procType) && Enum.IsDefined(typeof(ProcType), procType)))
{
throw new ArgumentException($"不正な起動タイプ:{procType}");
}
var list = Enumerable.Range(1, 30).ToList();
// c#でfall through はできないらしい。無念。
if (procType == ProcType.ELEGANT_REVERSE) list.Reverse();
switch (procType)
{
case ProcType.ELEGANT_REVERSE:
case ProcType.ELEGANT:
list.ForEach(d => Console.Write(IsMetamorphoseOjyohin(d) ? $"★{d}★," : $"{d},"));
return;
case ProcType.NORMAL:
list.ForEach(d => Console.Write($"{d},"));
return;
}
}
/**
* お上品になる条件の判定
**/
static bool IsMetamorphoseOjyohin(int num) => num % 3 == 0 || num.ToString().Contains("3");
}
}
まとめ
本日はナベアツプログラムを組んでみました。
技術ネタというより息抜きネタにはなりましたが、偶にはこんなネタもいいんじゃないかなと思います。
IT って頭使う職種ですし、偶には知性を投げ捨てるのも心の健康面で重要です。
ということで、是非皆さんも趣味全開のプログラムを書いてみてください。
遊び仕様とか、遊びの変数名とか仕込むと意外と楽しいですよ。