14:30以降になったら処理を辞めて停止するプログラムです。
一見すると良さそうに見えますが、このプログラムを15:00に動作させると30分は動作してしまいます。
この場合どのように書くのが最も適切でしょうか?
また、こういったミスをしないプログラムの書き方や勉強法があれば教えて下さい。
宜しくお願い致します。
#Perl
for(;;){
my ($sec,$min,$hour,$mday,$mon,$year,$wno) = localtime(time);
if($hour >= 14 && $min >= 30){
exit;
}
# 処理
sleep 1;
}
時刻のシリアル値で比較する。
my $now = time; my ($sec,$min,$hour,$mday,$mon,$year,$wno) = localtime($now); my $t1430 = timelocal($sec,30,14,$mday,$mon,$year); # 当日の 14:30 のシリアル値 if ($now >= $t1430) { ...
時分を四桁の整数にして比較する。
my ($sec,$min,$hour,$mday,$mon,$year,$wno) = localtime(time); my $now = $hour * 100 + $min; if ($now >= 1430) { ...
文字列で比較する、という手も(Perl だと無理矢理感 :-)
my ($sec,$min,$hour,$mday,$mon,$year,$wno) = localtime(time); my $now = strftime("%H%M", $sec,$min,$hour,$mday,$mon,$year); if ($now gt "1430") { ...
「ミスをしない書き方」というのは、なかなか難しいです。
この手の間違いは、プログラムを飯の種にしている人達が書くコードでもよくあります。
ここ、人力検索でも月単位のベストアンサーの集計で、月の末日がカウントされてない、という障害が一向に修正されません :-|
まずは、テストで問題点をどうやって拾うか、というところからアプローチするのが良いと思います。
まずは、「テストケース 境界値」というようなキーワードでググってみて、ひっかかったページとかを見てみると良いかも。
商売用のモードが入ってます。
趣味で書くプログラムだったら、自分でもここまでやらないだろうな、とは思います :-)
「ソースの書き方の勉強法」という意味では、他人のソースを読む、というのがあります。
だいたい、みんな 同じような地雷を踏んだ経験はあるわけで、多数の人が選んでいる書き方は、何かしらの効率の良さ(プログラムの処理速度とか、バグ取りの手間とかの諸々を含めて)の結果なんだろうと。
「定石」については、書籍が出てたり、ネット上にもあちこちに落ちてますが、当人にとっての定石なので、複数を参照して判断した方が良いでしょう。
将棋でも、「定石」はいくつもあって、棋士によってどれを使うかが分かれてたり、というのにも似ているような(ちょっと違うかも)。
また、そういった「定石」は、時代や環境によっても変わります。
先の回答に書いた「時刻のシリアル値を使う」というのも、C言語に代表される unix 系の時刻を扱うライブラリがシリアル値を使ったものだからこそ、ああいう書き方がやりやすいというだけです。
C言語が、あまりメインの開発環境ではなかった汎用機での COBOL や Fortran なんかだと、四桁の数値で扱う、というほうが主流だったはず。
要件は「14:30~23:59の間なら処理を停止(0:00~14:29の間は停止しない)」ですね。
a-kuma3さんの回答の通り、時と分を一つの数にまとめて比較するのが定石だと思います。
今のやり方で対処する場合、14:30-14:59と15:00以降に分ければ要件を満足します。
if($hour >= 15 || ($hour == 14 && $min >= 30)){
exit;
}
2016/1/28追記:
j4mikaさんからa-kuma3さんへの下記コメントを読んで、
確かにテストが最強だとは思いますが、論理的に抜け道のない方法であればテストしなくてもいいのになぁーと思ったりしています。でも、これがきっと難しいんですね。
「テストは不可避だが『テスト無しでも良い位、設計に自信を持ちたい』という気持ちは是非応援したい」と思ったものですから。
我々人間ですから、直感的で分かり易い表現にする工夫が大事だと思います。
例えば今回は時と分を別々の要素にしたので、2次元のグラフにすると分かり易いです。
if($hour >= 14 && $min >= 30)を表にすると
要件「14:30以降は停止」を満たしていない事が一目瞭然です。
抜け漏れの無い設計を支援する手法は色々あります。
下記に思いつくものを:
ベン図 - Wikipedia
真理値表 - Wikipedia
状態遷移表 - Wikipedia
統一モデリング言語 - Wikipedia
UMLはちょっと違うかな
a-kuma3さんのコメントを読んで確かにそうだと思いました。
jan8のコメントは、ホワイトボックステスト(MC/DC網羅)になってます。
「境界値テストはブラックボックステスト」その通りです。
「問題点をどうやって拾うか」が難しいですね。
1件当たりのテスト時間を減らすのも確かにその通りです。
1440通りのテストを自動化すれば、数秒で総当たりテストを完了できます。
回答いただきありがとうございます。
慣れの問題もあるのでしょうが、入門者の頃ですと、日常の感覚で考えてしまい、時間の比較程度で間違うことは無いという先入観がありました。14:30と15:15、どちらが大きいかを間違える人はいないですしね・・・ここに落とし穴があると気付かない気がするのです。
時間以外にも落とし穴を避けたり予め気付く方法があればいいなと思うところがあります。私自身、この落とし穴に長い間嵌っていましたので;;
また、グラフについてですが、ロジックで分かりやすく良いですね。ただ、これも慣れていないと考えることが増えてミスをしてしまいそうな気がしないでもないです。また、>=と>もたまに間違えることがあります。特に>は凄く苦手でいつも>=か実際に回してみてというのが多いです。Perlですとfor(1..100){}なんて出来るのですがあまりやっている人が居らず邪道なのでしょうかね・・・。個人的には直感的に出分かりやすく良い方法だと思っています。
設計支援について、ベン図や真理表については抜けを抽出できるため良さそうですね。状態遷移表、統一モデリング言語についてはかなり難しそうです。
時間のテストだけであれば、私の場合、パソコンの処理能力に物を言わせてシリアル値を無理矢理24時間分全て回してテストするかもしれません。省略できるところは省略する方が良いのでしょうが、省略したところに何か思いも寄らないミスが含まれていないか気になって仕方がないです。
横ヤリ→
2016/01/28 20:23:42テストケースの意味合いは為になります。グリニッジ標準時は恐れ入りました。テストの目的はバグを見つける事ですからね。かといって総当りは無理。そこで境界値・代表値ですね。
後付けですけどね :-)
2016/01/28 21:09:29ブラックボックステストとは言え、こういうふうに作ってるだろう、とか、こういう感じで間違ってるのはありがちだろうとか、想像力は働かせますよね。
長くやってると、ライブラリやコンパイラなどの処理系のバグにも出会ったりすることもありますし。