人力検索はてな
モバイル版を表示しています。PC版はこちら
i-mobile

こういったミスをしないプログラムの書き方や勉強法を教えて下さい。
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;
}



●質問者: j4mika
●カテゴリ:ウェブ制作
○ 状態 :終了
└ 回答数 : 2/2件

▽最新の回答へ

1 ● a-kuma3

時刻のシリアル値で比較する。

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") {
 ...


「ミスをしない書き方」というのは、なかなか難しいです。
この手の間違いは、プログラムを飯の種にしている人達が書くコードでもよくあります。
ここ、人力検索でも月単位のベストアンサーの集計で、月の末日がカウントされてない、という障害が一向に修正されません :-|

まずは、テストで問題点をどうやって拾うか、というところからアプローチするのが良いと思います。

はじめて学ぶソフトウェアのテスト技法

はじめて学ぶソフトウェアのテスト技法



まずは、「テストケース 境界値」というようなキーワードでググってみて、ひっかかったページとかを見てみると良いかも。




シリアル値を求めるメソッド名を間違ってたので、それの修正がてら追記です。

jan8 さんのコメントに書いたテストケース(9:00 近辺を足してます)の気持ちを。

商売用のモードが入ってます。
趣味で書くプログラムだったら、自分でもここまでやらないだろうな、とは思います :-)


「ソースの書き方の勉強法」という意味では、他人のソースを読む、というのがあります。
だいたい、みんな 同じような地雷を踏んだ経験はあるわけで、多数の人が選んでいる書き方は、何かしらの効率の良さ(プログラムの処理速度とか、バグ取りの手間とかの諸々を含めて)の結果なんだろうと。

「定石」については、書籍が出てたり、ネット上にもあちこちに落ちてますが、当人にとっての定石なので、複数を参照して判断した方が良いでしょう。
将棋でも、「定石」はいくつもあって、棋士によってどれを使うかが分かれてたり、というのにも似ているような(ちょっと違うかも)。

また、そういった「定石」は、時代や環境によっても変わります。
先の回答に書いた「時刻のシリアル値を使う」というのも、C言語に代表される unix 系の時刻を扱うライブラリがシリアル値を使ったものだからこそ、ああいう書き方がやりやすいというだけです。
C言語が、あまりメインの開発環境ではなかった汎用機での COBOL や Fortran なんかだと、四桁の数値で扱う、というほうが主流だったはず。


j4mikaさんのコメント
回答いただきありがとうございます。 4桁にする方法もあるのですね。これは気付きませんでした。私は無理矢理1秒に直すか誰かが作ったライブラリ(信頼性は不明)を使っていました。これですと年月日レベルでも対応できそうですね。 確かにテストが最強だとは思いますが、論理的に抜け道のない方法であればテストしなくてもいいのになぁーと思ったりしています。でも、これがきっと難しいんですね。 また、以前同じコードを書いてしまった時にはこれだけ単純な話でテストするのさえ馬鹿らしいと思っていました。どの部分をテストすべきなのかその判断が本当に難しいです。今回気付いたのは以前の教訓です。 また、他人のソースを読む方法が良いのですね。オープンソースなどやっている人は詳しい人が多いとも聞きます。因みに、Perlしか読めない、といっても他人のコードは驚くほど殆ど読めませんが(;;)、何かお薦めのサイトなどありますでしょうか?

a-kuma3さんのコメント
>> 確かにテストが最強だとは思いますが、論理的に抜け道のない方法であればテストしなくてもいいのになぁーと思ったりしています。でも、これがきっと難しいんですね。 << テストケースの抜けがあるので、「テストが最強」というわけでもなく。 ほんと、難しいです。 なので、「障害を起こしにくい書き方」と「障害をできるだけ拾えるテスト方法」は、車の両輪だったりします。 # 商売用のモードでは。 >> また、以前同じコードを書いてしまった時にはこれだけ単純な話でテストするのさえ馬鹿らしいと思っていました。 << 回答では偉そうなことを書いてますが、プライベートモードではテストはそこそこってことが多いです。 人力検索の回答でコードを書くこともちょいちょいありますが、何度も地雷を踏んでます <tt>:-)</tt> >> 因みに、Perlしか読めない、といっても他人のコードは驚くほど殆ど読めませんが(;;)、何かお薦めのサイトなどありますでしょうか? << CPAN のライブラリ。 オープンソースって、こういうのが良いなあ、と思います。 いろいろチューニングされてて難しいものも多いですけど、短いものもありますし、難解なものまで無理に理解する必要はないですし。 ぼくも、C言語を始めてステップアップしたくなったときに FreeBSD のソースを拾って読んだりしました。 コアな部分はさっぱり読めませんでしたが、ライブラリなんかは、読んでるうちにそこそこ理解できるようになります。

jan8さんのコメント
横ヤリ→ テストケースの意味合いは為になります。グリニッジ標準時は恐れ入りました。テストの目的はバグを見つける事ですからね。かといって総当りは無理。そこで境界値・代表値ですね。

a-kuma3さんのコメント
>> グリニッジ標準時は恐れ入りました。 << 後付けですけどね <tt>:-)</tt> ブラックボックステストとは言え、こういうふうに作ってるだろう、とか、こういう感じで間違ってるのはありがちだろうとか、想像力は働かせますよね。 長くやってると、ライブラリやコンパイラなどの処理系のバグにも出会ったりすることもありますし。

2 ● jan8

要件は「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)を表にすると
f:id:jan8:20160128195440j:image
要件「14:30以降は停止」を満たしていない事が一目瞭然です。

抜け漏れの無い設計を支援する手法は色々あります。
下記に思いつくものを:
ベン図 - Wikipedia
真理値表 - Wikipedia
状態遷移表 - Wikipedia
統一モデリング言語 - Wikipedia
UMLはちょっと違うかな


jan8さんのコメント
(論理的)ミスをしないプログラムの書き方 ・要件(目的)を厳密に記述する事 ・テスト工数の少ないロジックを記述する事 先の例で、1440通りの入力(00:00-23:59)全てについてテストする必要があるでしょうか? a-kuma3さんの「時刻のシリアル値で比較する」ロジックなら、14:29と14:30の2通りでテスト完了です。 jan8の「今のやり方で対処する場合」のロジックだと、13:30と14:29と14:30と15:00の4通りをテストする必要があります。なぜか分かりますか? テスト工数が少なくて済むやり方なら、間違える可能性のある箇所も減るので「ミスをしないプログラム」に繋がります。

a-kuma3さんのコメント
>> a-kuma3さんの「時刻のシリアル値で比較する」ロジックなら、14:29と14:30の2通りでテスト完了です。 << ぼくの理解とは、ちょっと違う。 テストでの境界値は、ブラックボックステストが基本。 なので、ぼくが書いたコードでも、jan8 さんが書いたコードでも、テストケースは同じ。 - 0:00 ちょうど、0:00±α - 8:00 ちょうど、8:00±α - 10:00 ちょうど、10:00±α - 12:00 ちょうど、12:00±α - 14:00 ちょうど、14:00±α - 14:30 ちょうど、14:30±α - 15:00 ちょうど、15:00±α - 16:00 ちょうど、16:00±α - 20:00 ちょうど、20:00±α(要らないかも) といった辺りがテストケースかなあ。 テスト工数の削減という意味では、テストケースを減らすのではなく、一件当たりの手間や時間を減らす。 この場合では、現時刻を取得するのに time を直接使うのではなくて、ラッパーを作る。 テストでは、ラッパーで境界値に当たる時刻を返すような関数に挿げ替える。 深夜まで待たなくても、現時刻が 0:00 近辺のテストができるようになります。 # テスト用に下位ルーチンを挿げ替えるのを「スタブ」と言います。 とはいえ、テストにかけるコストというのは、バランスの産物。 趣味の範囲でやるようなことだったら、どこまでやるのが良いのだろう、ということはあります。

jan8さんのコメント
a-kuma3さんのコメントを読んで確かにそうだと思いました。 jan8のコメントは、ホワイトボックステスト(MC/DC網羅)になってます。 「境界値テストはブラックボックステスト」その通りです。 「問題点をどうやって拾うか」が難しいですね。 1件当たりのテスト時間を減らすのも確かにその通りです。 1440通りのテストを自動化すれば、数秒で総当たりテストを完了できます。

j4mikaさんのコメント
回答いただきありがとうございます。 慣れの問題もあるのでしょうが、入門者の頃ですと、日常の感覚で考えてしまい、時間の比較程度で間違うことは無いという先入観がありました。14:30と15:15、どちらが大きいかを間違える人はいないですしね・・・ここに落とし穴があると気付かない気がするのです。 時間以外にも落とし穴を避けたり予め気付く方法があればいいなと思うところがあります。私自身、この落とし穴に長い間嵌っていましたので;; また、グラフについてですが、ロジックで分かりやすく良いですね。ただ、これも慣れていないと考えることが増えてミスをしてしまいそうな気がしないでもないです。また、>=と>もたまに間違えることがあります。特に>は凄く苦手でいつも>=か実際に回してみてというのが多いです。Perlですとfor(1..100){}なんて出来るのですがあまりやっている人が居らず邪道なのでしょうかね・・・。個人的には直感的に出分かりやすく良い方法だと思っています。 設計支援について、ベン図や真理表については抜けを抽出できるため良さそうですね。状態遷移表、統一モデリング言語についてはかなり難しそうです。 時間のテストだけであれば、私の場合、パソコンの処理能力に物を言わせてシリアル値を無理矢理24時間分全て回してテストするかもしれません。省略できるところは省略する方が良いのでしょうが、省略したところに何か思いも寄らないミスが含まれていないか気になって仕方がないです。
関連質問

●質問をもっと探す●



0.人力検索はてなトップ
8.このページを友達に紹介
9.このページの先頭へ
対応機種一覧
お問い合わせ
ヘルプ/お知らせ
ログイン
無料ユーザー登録
はてなトップ