プログラミング初心者のための試行錯誤ではないデバッグの進め方
はじめに
PCひとつあれば自分の作りたいものを自由に作れるプログラミングは楽しいものですが、いざ実行となると期待通りには動かないもの。ここから辛いデバッグの時間が始まります。
いわゆるプログラミング教育の文脈ではなぜか「デバッグ = 試行錯誤」と捉えられていることがあり、辛抱強く取り組む忍耐強さを身につけるのに有用という謎理論が展開されることもあります。辛いことを経験することが学びにつながるという考え方はまだまだ根強くあるようです。
しかし、デバッグというのは試行錯誤ではありません。もし職業プログラマーが試行錯誤だけでデバッグをやっていたら職業人としては失格。仮にそれで動いたとしても、そのプログラムは信頼性が低いものになってしまいます。
ここでは試行錯誤に陥らない、プログラミング初心者のためのデバッグのポイントについて案内していきます。
バグとは?
そもそもバグとはなにか?ということからお話していきましょう。ここでは人間にたとえてみます。
あなたは今、外出先(学校とか会社とか)にいます。家に帰ろうとしている時、家族からメッセージがきました。そのメッセージには「帰りにパン屋さんで食パンと牛乳を買ってきて」と書かれていました。
これをプログラミングにたとえると以下のようになります。
- あなた:コンピュータ
- メッセージ:プログラム
- 家族:プログラマー
つまり、あなたは家族にプログラミングされたロボットのようなものだとイメージしてください。当然ですが、メッセージの内容に間違いがあればあなたは家族の期待した通りにうまく動くことができません。これがバグです。
a. メッセージが「帰りにパン屋さんで食パンと牛乳をってきて」だった時
このメッセージでは「買」という文字が抜けているので、何をすればよいかわかりません。あなたがコンピュータだとすると、どうしていいのかわからないのでここでエラーが発生して止まってしまいます。
これはいわゆる文法エラーというエラーで、プログラム(メッセージ)が正しく入力されていないことによって発生する不具合です。
もちろん、本来のあなたは人間なのでこれがおかしいと気づいたら、文章の意味を読み取って「買ってきて」という意味だと理解できるかもしれませんし、メッセージに返信して正しい内容を送り直してもらうこともできると思います。
しかし、コンピュータは指示された通りにしか動かないので指示内容がわからないとその時点でエラーが発生してしまうのです、ここが人間とコンピュータの違いです。
b. メッセージが「帰りにパン屋さんで食パンと牛乳をとってきて」だった時
このメッセージの内容をそのまま受け取ると、パン屋さんに言って食パンと牛乳を取ってくることになります。意味としては正しいです、パン屋さんにいってお店にある食パンと牛乳をとってくることはできます。なので、あなた(コンピュータ)はこの指示通りに行動することができます。
しかし、パン屋さんでお金を払わずに食パンと牛乳をとってきたら泥棒ですよね。プログラムは動くけれど、その動作は正しくありません。これもバグです。
同じく、本来のあなたは人間なのでここでおかしいと気づけるはずです。まさか、本当にパン屋さんでお金を払わずに食パンと牛乳を取ってくることはないでしょう。でも、コンピュータはそうしてしまうのです。
c. メッセージが「帰りにパン屋さんでパンと牛乳を買ってきて」だった時
このメッセージを受け取った時には、家族が食パンを買ってきて欲しいのだということがわからないので、あなたは自分の好きなパン、食べたいパンを買ってくるかもしれません。もちろん、家族は食パンを買ってきて欲しいと思っているので、あなたが買ってきたパンは家族の期待とは違います。これもまたバグのひとつです。
さすがにここまでバグってしまう(間違ってしまう)と人間のあなたでも察することはできないでしょう。
これらがバグです
ここで示したものがプログラムのバグです。プログラムが正しく動作しないケースとしては以下の通り2つに分類されます。
- コンピュータがプログラムの意味を理解できない(aのケースがこれにあたります)
- コンピュータはプログラムの意味を理解できるけれど、意味が正しく伝えられていない(bとcがこれにあたります)
1のバグはエラーメッセージが表示されるのでまだわかりやすいのですが、2のバグは原因を見つけるのが難しいことがあります。
もし、ここまでを読んで「牛乳を1つ買ってきてちょうだい。卵があったら6つお願い」の話を思い出したなら、あなたは十分ベテランのプログラマーのはずなので、ここから先を読む必要はないかもしれません。
バグの原因をみつけよう
あなたが写経段階ならプログラムを見比べましょう
プログラミングの世界には写経という言葉があります。これは、他の人が作ったプログラムを見ながら打ち込むことで、プログラミング初学者はここから入ることも多いかと思います。
もしあなたが、まだプログラミングを始めたばかりで、写経をしている段階だったら、まずは元のソースコードを読み返すことをしっかりやっていきましょう。元のソースコードと見比べることで、自分がどこを間違えたのかを見つけることができるはずですし、それ以上のことはできないかもしれません。
私もプログラミングを始めたばかりの頃は写経でした。当時はスマートフォンはもちろんインターネットもなく、無料アプリのダウンロードなんて影も形もありません。なので、アプリ(ソフトウェア)を手に入れるには買うか自分で作る、もしくは本に載っているものを自分で入力するしかなかったのです(当時はプログラムのソースコードが掲載された本がたくさんありました)。
でも、それを繰り返しているうちにパターンが見えるようになってきました。こんな動作を作りたい時にはこう作ればいいというのが分かるようになってきたのです。それからは、自分の持っている機種に合わせてプログラムを変更(移植といいます)することもできるようになりました。当時はメーカーによって少しずつ言語仕様が違っていたのです。
余談が長くなりました、写経をしている段階なら、まずは元のソースコードと見比べることををお勧めします。
エラーメッセージが出ていればメッセージを読みましょう
プログラムを実行した結果エラーメッセージが表示された場合(上に書いた例でいうとaのケースのようなバグです)まずはエラーメッセージを読みましょう。エラーメッセージはプログラムがどこでどのようなエラーを起こしているのかを教えてくれるものです。プログラムの何行目でどんな問題が発生しているのかを教えてくれます。まずはこれを読まないことには話になりません。
しかし、プログラミング初心者はエラーメッセージを読まずに「動きません、なぜでしょう」と私に言ってくることもよくあります。いえいえ、私にだってわかりません。まずはエラーメッセージを読んでください。エラーメッセージがポップアップウインドウ(ダイアログ)で表示された時、反射的に閉じてしまう人も珍しくはありません。エラーメッセージを読むのが怖いのでしょうか。
確かにエラーメッセージは英語で表示されることが多いし、専門用語も多いので読むのが難しいかもしれません、プログラミング初心者には難しすぎる、気持ちはわかります。でも、それを読まないことには話にならないのです。エラーメッセージをコピーしてGoogleで検索するだけでも原因が分かるかもしれません。
一番多いのは"Syntax Error"というやつです、上の例でいうと「帰りにパン屋さんで食パンと牛乳をってきて」みたいなやつです。
日本語でいうと文法エラーですが原因は単なる打ち間違いであることがほとんどです。Syntax Errorが出たらまずはエラーが発生した行に入力ミスがないか確認しましょう。
ただ、ちょっとやっかいなのは必ずしもエラーメッセージで示された行に間違いがあるとは限らないことです。別の行の間違いのせいでその次の行でエラーが発生していることもあります。これは特定のケースでのみ発生しますが、これが分かるようになるには経験が必要です。
エラーメッセージが出なかったらあるべき姿と現状を整理しましょう
エラーメッセージが出ない時というのは「コンピュータはプログラムの意味を理解できるけれど、意味が正しく伝えられていない」時ですね。これはかなりやっかいです。デバッグで苦しむほとんどのケースはこちらです。そういう時は、まず以下の点を整理しておきます。
- 本来あるべき動作はなにか
- 今実際にどんな動作が起こっているか
そんなことわかってる、と言われるかもしれませんが、意外と正しく把握できていないこともあります。とくに大きなプログラムだと1を明確に把握できていないこともありますし、あるべき動作だと思っていたことそのものが間違っていたということもあります。
まずは、この2つをきちんと整理してからデバッグに取りかかりましょう。そうしないと時間を無駄にしてしまうかもしれません。
まずは思い当たるところを考えてみましょう
まず最初に思い当たるところがないか考えてみます。正しい動作をしていない箇所が、表示なのか入力なのかデータの保存なのか、それによってどこが関係していいそうかのおおよその見当がつくはずです。
もしかすると、その中に「ちょっと自信がないところ」「よくわからないけど、ネットで見つけたコードを貼り付けただけのところ」があったら、真っ先にそこを確認しましょう。手を抜いたところはほぼ確実にバグが発生します。自分のプログラムでわからないところがちゃんと動いているなんてことはないのです。
手を抜いたところがあれば、そこをきちんと見直して自分が理解できるレベルには持っていかなければなりません。
発生条件を絞り込みましょう
つぎに発生条件を絞り込みましょう。100%発生するならわかりやすいのですが、たまにしか発生しない場合は原因を見つけるのが難しいです。たまにしか発生しない場合は、その条件を再現するための手順を考えてみましょう。たとえば、特定の操作の時だけ発生するのかもしれません、時間かもしれない、データの中身かもしれませんし、それらの組み合わせかもしれません。
再現とは同じ動作を起こさせることです。デバッグの場合は正しくない動作を発生させることを「バグの再現」といいます。
プログラムの動作を見ているだけではわからないこともあるかもしれません。その場合は、プログラムに細工をして動作途中の情報を出力してみるという方法もあります。これをデバッグプリントとかログ出力といいます。デバッグプリントを入れることで、プログラムのどこでどんな値が入っているのか、どんな経路を通っているのかを確認することができます。
条件分岐などで自分が想定していた通りの分岐をとっていないかもしれません。そういったこともデバッグプリントを入れることで確認することができます。
発生条件がわかったら再現を確認しましょう
発生条件がわかったら、その条件で再現することを確認しましょう。バグの再現ができれば、その条件で何が起こっているのかを確認することができます。
発生条件によっては、人間の操作で再現させることが難しいかもしれません。その場合も、プログラムに少し細工をするというやり方があります。
たとえば、ある変数の値が10の時だけ発生するのではないか?という仮説(あくまでも仮説です)があったとします。その変数が10になる状態にするための操作(たとえば、ある操作を10回繰り返したら発生するなど)に時間がかかるような場合は、一時的に強制的に変数を10にする処理をプログラムの中に組み込んで再現を確認するのです。
プログラムを変更して再現することが確認できたら、プログラムを元に戻して実際の操作で再現することを確認しましょう。プログラムを変更したのは、あくまでも仮説の正しさを手っ取り早く確認するためのものであって本来の動作ではありません。
プログラムの変更は慎重に行なってください、プログラムの変更はバグの元です。ここで別のバグを作り込んでしまっては意味がありません。こういう時にGitを使っていると一時的にプログラムを変更し、確実に元に戻せるので便利です。もし、GitをつかっていなければGitの利用も検討してみてください。
バグを修正しよう
発生条件と再現方法がわかれば原因もほぼわかったはずです。原因がわかれば、次に考えることは修正方法です。ひとつのバグの修正方法は多くの場合複数あります。この時、最初に思いついた修正方法を採用するのではなく、他にも考えられる修正方法がないかを考えてみましょう。ポイントは以下の通りです。
- もっと少ない修正で済む方法がないか
- 他の箇所に影響を及ぼさないか
- 他のバグを作り込んでいないか
- ここで修正するのが正しいのか、別のところで対処すべきではないのか
初心者には難しいかもしれませんが、修正方法を考えることもデバッグの一部です。修正方法を考えることで、プログラムの理解が深ま、プログラムの設計がよりよくなり、プログラムの品質が向上します。
修正したら再度テストを行いましょう
プログラムを修正したら、もう一度再現確認をしましょう。ここでは再現確認ためのプログラム変更は厳禁です、あくまでも本来の操作、手順で再現しないことの確認が必要です。
まとめ
もっとコンパクトな話になるつもりで書いていたのですが、書き出すと思ったよりボリュームアップしてしまいました。
プログラミングの経験値が上がっていくと、ここに書いた方法以外のテクニックも身につくでしょう。でも、まずはここに書いた方法を参考にデバッグを進めてみてください。少なくとも「試行錯誤」で苦しむようなことにはならないとは思います。
では、楽しいプログラミングとデバッグを!
Discussion