Home -> HSP講座 -> 初級編 No.14

バグよ……消えろっ!

ここでは、ちょっと方向性を変えて上大流デバッグの仕方
紹介していこうと思います。


「バグ」の定義

バグ(bug)とは、「プログラムが、プログラマが望んでいない、想定外の動作をすること」です。
例えば、次のスクリプトにはバグが存在します。

// バグのあるサンプル
// 0 〜 MAX_NUM - 1 までの数値をランダムに並び替える

#const MAX_NUM 12

*main
	gosub *Initialize	// 初期化
	gosub *ClearVariable	// 変数を初期化
	gosub *ShuffleArray	// 配列の中身をかき混ぜる
	gosub *ShowArray	// 表示
	stop
	
// 初期化
*Initialize
	randomize
	return
	
// 変数を初期化
*ClearVariable
	dim array, MAX_NUM
	
	// array に初期値をセット
	foreach array
		array(cnt) = cnt
	loop
	return
	
// 配列の中身をかき混ぜる
*ShuffleArray
	foreach array
		n          = rnd(MAX_NUM)	// 適当な番号
		tmp        = array(n)		// array(n) と array(cnt) を入れ替える
		array(cnt) = tmp
		array(n)   = array(cnt)
	loop
	return
	
// 配列の中身をすべて表示する
*ShowArray
	foreach array
		mes "array("+ cnt +") = "+ array(cnt)
	loop
	return

前回の配列変数を使用しています。
このサンプルの意味が分からなければ、復習した方がいいでしょう。

おそらくこれを書いたプログラマ(ここでは僕)は、0 〜 11 がランダムな順番になるように
したかったのでしょう。しかし、実際には同じ数値が何度も出てきてしまっています。
さて、これのバグを見つけられましたか?
この後で、デバッグという作業を行い、正常に動作するようにします。


「デバッグ」の定義

デバッグとは、「プログラムからバグをなくすために、ソースコードを編集すること」です。
広い意味(広義)では、バグの原因を見つける作業も含めます。
デバッグを行うための方法は無数にありますし、どの方法が最適かは誰にも分かりませんが、
常套手段がいくつかあります。
ここでは、それらをいくつか紹介していきます。


スクリプトを読む

当然です。バグがあったら、スクリプトを読むに決まっています。
このときは、(必要なら) gosub 先のラベルの中まで、しっかりと見渡し、常な記述を探します。( 深夜に書いたスクリプトはケアレスミスよりアホミスの場合が多いです )
パラメータの順番が違うとか、変数が間違えているとか、命令を勘違いしているとかです。
10分くらいは根気よく探しましょう。どうでも良いミスなんかはすぐに見つかります。
大切なのは、自分を過信しないことです。人間は間違いを犯しやすいので、「絶対に失敗していない」なんてことは絶対にありません。
慎重に確認しながら読み進みていきましょう。
※同じスクリプトを何回も読んだって、なにも出てきません。素直に他の方法を使いましょう。


変数値の出力

バグの原因を見つけるためによく行われるのは、変数の中身を監視することです。
理由としては、変数の値が異常なせいでバグが起こっていることが多いからです。
目に見えない変数の値を、外に出力し、見えるようにしてやりましょう。

出力する方法は、4 つです。
一つ目は、mes命令によって、クライアント領域に表示させる方法ですが、あまり意味はないでしょう。見えなかったら終わりですし、スクリプトを荒らしてしまいます。
つまり、mesが見えるようにpos命令などを実行してスクリプトの意味を大きく変えてしまっています。これでは、逆にバグを生みかねません。
この方法はやめた方がいいです。

二つ目の、title命令もお奨めできません。
mesのような弊害[へいがい]はありませんが、表示できる量が少なすぎます。

結局三つ目もダメです。ファイルに出力する方法ですが、面倒でしょう。

四つ目はとても良い方法です。logmes命令という、特殊な命令を使います。
これは、デバッグ専用の命令です。デバッグ時に、文字列を出力したいときに使用します。
mesと同じ感じで使えますが、logmesのパラメータは文字列でなければいけません。
続きは↓。


デバッグウィンドウ

HSPは取っつきやすい言語だと言われていますが、その理由の一つが、デバッグウィンドウ(DebugWindow)機能の存在ではないかと思います。(勝手に思ってます)
これはデバッグを助けるためのソフトで、一般的にデバッガと言われているソフトの一種です。
使用するために、メニューバーの「HSP」→「デバッグウィンドウを使用する」をクリックして、チェックを付けてください。
その状態で実行(F5)すると、Debug Window というウィンドウが出現しましたね?
DebugWindow
※内容は違うかもしれません。
「内容」は、スクリプト全体とシステム変数についてですが、今はどうでもいいです。

とりあえず最初のサンプルを実行してみましょう。
DebugWindow
「変数」は、スクリプトで使われている変数が列挙されています。
変数名をクリックすると、その変数の状態を知ることができます。
これは大いに役立つ機能です。最大限に活用しましょう。

そして、最後に「ログ」です。
ここには、logmes命令で出力される文字列が表示されます。
この領域はデバッグ専用の領域なので、通常の実行を邪魔することはありません。
さっそく、ログに出力してみましょう!

// logmes を使ってみる

*main
	repeat
		logmes ""+ cnt	// logmes cnt では×
		await 17
	loop
	stop

一見なにも起こりませんが、「ログ」を見ると、ひたすら数値が出力されているのが分かります。
DebugWindow
このように、logmes命令は、実行するたびに改行してくれます。
これで array の内容などを、出力してみましょう。

// 配列の中身をかき混ぜる
*ShuffleArray
	foreach array
		n          = rnd(MAX_NUM)	// 適当な番号
		tmp        = array(n)		// array(n) と array(cnt) を入れ替える
		array(cnt) = tmp
		array(n)   = array(cnt)
		
		logmes "cnt = "+ cnt +" : n = "+ n
		
	loop
	return

※常套手段その1

logmes を追加しただけです。
さ、「ログ」タブを見てみましょう!
DebugWindow
意味がなかったようですね……。
失敗も経験です、別のモノも出力してみましょう。

// 配列の中身をかき混ぜる
*ShuffleArray
	foreach array
		n          = rnd(MAX_NUM)	// 適当な番号
		tmp        = array(n)		// array(n) と array(cnt) を入れ替える
		logmes "tmp\t= "+ tmp
		array(cnt) = tmp
		array(n)   = array(cnt)
		logmes "array(n)\t= "+ array(n)
	loop
	return

array(n) と array(cnt) を入れ替える辺りを疑い、array(n) と tmp を出力してみました。
\t(タブスペース)を使っているのは、= の位置をそろえ、見やすくするためです。
※見やすいときと、見やすくないときでは、結果から得るものが違います。
 見やすいときの方が多くのことが見えてくるでしょう。
結果は……
DebugWindow
ありゃりゃ、同じになってる。入れ替えるつもりが、同じにしてしまっていたんですね。
ここまで来れば、原因は一目瞭然です。


第三次上大内閣成立を表明、それなんて大惨事!

※まぁ、題は無視してください。

HSPでは、logmes 命令ともう一つ、assert 命令というものも使用できます。
これは、(条件付きで)一時的に実行を中断し、デバッグウィンドウを表示する命令です。
デバッグウィンドウの「実行」ボタンで再開できます。
意味的には「わたしはこの式が成立することを表明する!」という感じで、成立しなかったら( 偽なら )「なぜだっ!?」と、止まってしまいます。
詳しくはワンキー・ヘルプ(F1)を見てください。


コメントアウト

どの辺りでバグが発生しているのかが分かったら、一行一行見ていきます。
そのとき、一部のスクリプトを一時的にコメントにしてしまい、それでバグが発生しなければ、コメントにした部分が異常、ということになります。
このように、一時的にスクリプトをコメントにしてしまうことを、コメントアウトする、といいます。
一応コメントとして残っているので、簡単に復帰させることが可能です。


と、まあ、デバッグウィンドウの操作方法と見方を紹介したので、目的は達しました。
変数出力の方法とコメントアウトしか紹介しませんでしたが……。
デバッグは、慣れです。習うより慣れろというヤツですよ。
;OoO) < それに、たまには講座を短くしたいですしねぇ。

では、また次回。


by 上大

第十三章へ