条件式というのは何かと複雑になってくるものです。
1つ1つはシンプルなはず。なのになぜ?
そこを複数の視点から解明して実用的に使っていたら3個に集約されました。
プログラムの条件式をシンプルにする2つだけの手法
条件式は除外ケースから先に処理(書いて)いく
例えば、stringを正の整数にする関数でint型で返すとします。
仕様としては数字のみで、小数点「.」があれば切り捨てる。「-」その他の文字があれば仕様外ということで常に -1 を返す、とします。また0は許容(OK)とします。
また、nullは-1で、""(空文字)は0とします。
tryparse関数がありますが、あえてparse関数を利用するものとして、tryも利用しないこととします。
順序としては
1. nullならreturn -1
2. 0と1~9と「.」以外の文字があればreutrn -1
3. 「.」があれば、stringの最初の「.」以降の右側を除去する。
4. 先頭に0があれば除去する
5. ""ならreturn 0
上記1~5を行っておくことで、ここに処理が遷移してきた段階では数字のみで来ます。
例外エラーが生じない状態で、parse関数を使えますし、万が一にも例外エラーが出るケースがあれば、それに対応できます。
多くのケースではtry~catchでよいのですが、例外の内容によって細かく処理が分かれるようなケースでは
このように除外するケースを先に書いておく、というのがとても有用でした。
関数内の諸条件ではローカル関数もしくは匿名関数として処理する
VisualStudio2013の頃から、個人的には匿名関数が好きになってしまったのでご紹介します。
感覚としては関数の中に、その関数専用の特化チョイ関数を書く簡便さで使っています。
今では(C#7からは)ローカル関数としてシンプルに記述できます。(コメントで教えて頂きました)
匿名関数では
Func<bool, string>is_correct_value = ( in_text) =>
{
};
だったのが、ローカル関数では
bool is_correct_value(string in_text)
{
}
と、普通にかけます。
private int get_positive_integer(string in_int_text)
{
Func<bool, string>is_correct_value = ( in_text) =>
{
if(in_text == null)
return false;
if(Regex.IsMatch("[^0-9.]", in_text)
return false;
return true;
};
if(is_corrct_value(in_int_text)==false)
return -1;
string tmp_text = in_int_text;
if(tmp_text.Contains(".")==true)
tmp_int_text = tmp_int_text.Substring(0, tmp_int_text.IndexOf('.'));
if(tmp_int_text == "")
return 0;
int ret_value = int.Parse(tmp_int);
return ret_value;
}
のような感じです。
ローカル関数なら IEnumerable内でも yield return を使える
yieldのおかげで複雑な処理を複数段に分けて、簡潔な処理記述にできるのですが、
匿名関数のときは IEnumerable の中で匿名関数が使えずに「仕方ないな」と思っていました。
ですが、ローカル関数ではいけるのでもっと早くにあればよかった、という感じです。
(匿名関数が使えなくても専属のprivate関数を書いた程度の違いではありますが、数ヶ月後の可読性というか
思い出しやすさが違います(汗)
ちなにみyieldは個人的には処理手順の段階化(レイヤーを分けられる)・単純化に有用だなと心底思っています。この機能のおかげでオフコン世代のインタープリタ記述のものをプリンタの独自命令にまで変遷させていく、というプログラムを書くことができました。(次の人に説明しやすい構成(のはず・・・)で書けました)