<< 豆知識 >>エラーへの対処法プログラミングにはエラーがつき物です。どんなプログラマーだって、一度もエラーをしたことは絶対ないし、また全然エラーをしなくなることだってきっとないと思います。その数は少なくなったって、どうしたってエラーとはうまくお付き合いしていかなければなりません。 エラーはプログラマのミスによって起こります。特にコンパイル時のエラーはそうです。実行時のエラーにはコンピュータが原因で起こるものがあるかもしれませんが、コンパイル時は文法の間違いで引き起こるものがほとんどです。全てがプログラマが原因のエラーであるわけではありませんけどね。例えばFortran77のコンパイラにFortran90の文法を持ち込めばエラーになってしまいますから、バージョンをしっかり意識していなかったプログラマが悪いといえば悪いのですが、コンパイラが古いのが問題です。ともかく、どうやったらエラーをなくしていけるでしょうか。あるいは少なくしていけるでしょうか。完璧な方法があれば当然いいのですが、どうしても最後はプログラマ次第になってしまいます。それでも少なくする心構え、ガイドラインは存在しますから、それを実践していきましょう。もちろんFortranの話ですが。 まずどうやってソースコードを作成していったらいいかの話です。Fortranのサイトは数少ないながらもそれなりにあるので、それらを見て回ったところ、いくつか共通する見解がありましたので、それを紹介したいと思います。僕の経験も踏まえてお話します。 まず、自由形式で書きましょう。Fortranには77以前の固定形式と、90以降の自由形式とにプログラミングスタイルが分かれますが、77の方法は忘れてください。固定形式は非常に使いづらいです(77のコンパイラを使用している人には無理な話ですが)。固定形式の特徴は、一行にかけるのが72欄までで、始めの5欄までは文番号用、6欄は文の折り返しを示す記号用として決められていて、実際に文を書き始めることができるのは7欄からです。実質66個しか文字を書くことができません。プログラムによっては問題ないですが、いささか少なすぎます。そして書ききれなかったら折り返すのですが、折り返すときには6欄にその目印の記号を書かなければなりません。タブを使ってインデントしている場合に、この処理はとても嫌なものに感じてきます。なぜなら、折り返す前のインデントの位置に継続文を移動させると、折り返し記号「+」との間に空白が生じてしまい、折り返す前の文との間に空白が入り込んでしまうからです。このように、「固定」形式というだけあって、その使い勝手はかなり制限の加えられたものであるということができます。 自由形式にすれば、「欄」という概念がなくなり、1行132列まで書くことが可能になります。要は固定形式の2倍の文量を一行に書くことが可能となるわけです。さらにどの位置から書き始めることも可能になり、折り返し記号「&」もどの位置に書いても構わなくなったので、自由なソース作成が実現します。コメントも固定形式では1行丸まる使用しなければならなかったのが、命令文の右側に付けることができるようになるので、ソース文を邪魔することなく、横側に添える形で説明を加えることが可能になります。セミコロンを挟めば一行に何個でも命令文を圧縮することが可能なのも便利です。 次に挙げておきたいのが、文番号の使用をやめようということです。文番号はGOTO文やFORMAT文で使用される概念ですが、これがエラーの元になることが多いようです。文番号を使用する場合は、今どの文番号まで使用していたのかを確認する作業が面倒くさく、もし間違えて既に番っている番号を使用してしまえばエラーになってしまい、間違った位置に番号をつけてしまえば実行エラーの原因探しに一苦労となってしまいます。FORMATぐらいでしたら、WRITE文やPRINT文の下にあるからいいでしょうが、GOTO文の場合はどこに向かって飛んでいるのかが分かりにくいですし、飛び先の番号の位置があっているのかが分かりにくい場合があります。また文番号として確認したうえで20番を書こうとしていたのに、勘違いで10番と書いてしまうこともありますので、文番号の使用はやめたほうがいいです。幸いFortranも文番号なしで十分プログラムがかけるぐらいに成長しています。以前は条件によりループを途中で抜け出したいときはGOTO文しかありませんでしたが、今はEXIT文があり、DO文にループ名をつけておけば、多重ループから一気に抜ける事だって可能です。CONTINUEも同様です。ENDDO文があるのですから、不要で面倒くさい文番号の必要なCONTINUE文はやめましょう。入れ子が多すぎで、このENDDO文はどのDO文と対をなしているのか分からないようであれば、コメントをつけておくとか、DO構文名を書いておけば大丈夫です。FORMAT文も出力文に省略して書くことで、文番号なしで書式付出力を行なうこともできるようになっているので、文番号がなくても十分プログラムは組めます。ダイクストラの頃から批判の多かった文番号は、使うのをやめましょう。 Fortran特有の機能である暗黙の型宣言がありますね。これは使用すべきではありません。確かに便利な面はあります。例えばカウンタとして僕はiやjをよく使用しています。これらは一文字ですから、打ち間違えしないので、暗黙の型宣言の不利益を被ることなく、わざわざintegerで型宣言をすることなく使用できます。このスムーズなソース作成を支援するのが暗黙の型宣言です。しかし、暗黙の型宣言は変数名の打ち間違いを、新しい変数の作成と誤認してしまい、コンパイラがエラーを発見しません。最初「ia」と使っていて、次に「la」と書き間違えてしまっても、コンパイルが気付いてくれないので、明らかに意図しない実行結果を得ることになってしまいます。また「kazu」は整数型であるにもかかわらず、意識せず実数型の変数と用いてしまうかもしれません。僕のコンパイラは入力後は引用、宣言後は入力しないとコンパイルエラーに引っ掛かるのである程度はコンパイル時に気付くことができますが、なるべくこうしたつまらないエラーは起こさないようにするために、暗黙の型宣言は封印するべきです。封印の仕方は知っていますね。IMPLICIT文を用います。プログラム単位の先頭に「implicit none」と書けば、暗黙の型宣言は使用されず、常に明示的な型宣言が必要になります。変数を用意する際に必ずプログラム単位の先頭に戻って変数を宣言しなければならず、手間は増えてしまいますが、その代わりエラーに悩まされる数はかなり少なくなります。 副プログラムを使用するのであれば、INTENT文を使用しましょう。Fortran90では副プログラムのエラー排除のための手段として、INTENT文が追加されました。この文を副プログラムの仮引数の宣言文の際に用いれば、副プログラムへの入力用なのか、出力用なのか、その両方なのかをハッキリとさせることが可能です。仮引数が一つだけで、入力兼出力なのであれば、この機能を利用したほうがいいと思います。INTENT文を使用すれば、コンパイラに対してその引数の役割を明確に伝えることができ、ソース文を読む人間にとっても引数の役割がわかりやすくなり、可読性の向上につながります。 字下げもしっかり行ないましょう。やはり人間は視覚的要素を非常に重要視します。人のよさを見るときに、一番重要視するのは見た目ですよね。いい女かどうかを見分けるとき、大抵の人は顔を見ると思います。それと同じで、字下げをすることによって縦方向にそろっているプログラム文は、見た目がかっこよくなるだけでなく、上と下のつながりが非常に分かりやすくなります。全く字下げをしない文章とした文章をくらべてみれば、その差は歴然であると思います。字下げをする場所は、各プログラム単位のないよう部分、DO構文内、IF構文内などです。とくにDOとIFで字下げをしないと、大変なことになります。 後は、コメントを付けておきましょう。コメントは「!」をつければ、ソース文の横に書くことが可能です。いくら構造化プログラミングを意識したり、字下げをしたとして分かりやすいプログラムを書いたとしても、プログラムはやはり分かりにくいものです。他人のであれば特にそうです。自分のためにも他人のためにも、必要最低限のコメントは付けておきましょう。簡単なもので構いません。この位置から何々が始まるとか、この副プログラムは何々をするものだ、ということを書くだけでも、見返したときにグンと理解しやすくなります。 続いて、実際にプログラミングをしたときのどんなエラーが出て、どういう風に対処したらいいのかを紹介したいと思います。人によってエラーの数は違いますし、下のだけではありませんが、したのを意識するだけでもエラー処理がかなり楽になるのではないのかと思います。
コンパイルエラーは大抵コンパイルが示すエラーの箇所を見れば理解できるのですが、実行時のエラーはなかなか分かりません。実行時のエラーとして、上に示したような「配列の大きさが違う」のような場合は何行目かが表示されるのでいいのですが、実行はされるけれども思い通りの実行がされていない(例えば、1+2の結果が10になってしまうとか)場合もあります。また大部分の実行はうまくいくけど、一部分についておかしな動きをする(例えば車が壁に突っ込んでもすり抜けてしまう)バグが起きることもあります。こうしたエラーはその原因の理由・原因箇所を非常に見つけるのが難しいです。そこで、デバッガを持っている人は、ぜひそれを使ってデバッグしてみてください。かなりそうしたエラーが見つけやすくなります。 デバッガのよいところは大きく分けて2つあり、一つは任意のところで実行をとめることができることと、変数の値の変化が把握できるということです。全てのデバッガにそれが備わっているのかどうかは分かりませんが、僕が持っている「Fortran&C」を元にお話させていただきます。 デバッガの利点の一つ目についてですが、任意のところで実行をとめることができるというのは非常に便利です。僕のデバッガは、実行の仕方に2種類あって、中断点というのを実行をとめたい行に打ち、そこまで一気に実行する方法と、一行ずつ実行していく方法とがあります。プログラムをそのまま実行してしまうと、最初から最後まで一気に進んでしまいます(READ文がなければ)。しかしデバッガを使用すれば、実行を見たいところまで勧め、見たいところから一行ずつゆっくり進めていくということが可能になります。例えばIF分岐を考えてみてください。明らかにプログラムの実行の仕方がおかしいが、その原因はIF文の分岐の仕方がおかしいからじゃないかと感じたとします。実行をIF文の前で止め、そこから一行ずつ実行していきます。そうすることで、自分の考えたとおりにIF文野下に進んでいるのか、それともELSE文に飛んでしまっているのかを確認することができます。DO構文でも同じ活用の仕方ができます。しっかり決めた回数分ループしているのか、EXITで条件通りにループを抜け出しているのかを、ゆっくり実行することで確認することができます。そこで変な動きをすれば、そうなる原因を考えればいいのです。デバッガを使用しないと、ソース文でプログラムの動きをイメージしなければならないのですが、デバッガがあれば、実際にプログラムの動きを見ることができ、便利です。 もう一つの利点は、変数内の値の変化を見ることができる点です。Fortranは破壊的代入をしているので、同じ変数でもどんどん新しい値になって行きます。ループを使用した合計を求めるプログラムをイメージしてもらえれば分かりやすいと思います。プログラムを実行した結果、思い通りの値にならなかったのを解決する場合、上のように少しずつ実行してどういう動きをしているのかを見るほかに、変数内の値の動きを追うことでぐんとわかりやすくなります。例えば、EXITが作動するには基本的にIF文の条件式がうまく作動することが必須ですよね。aが3になったらループを抜ける場合、「if(a==3)exit」とかきます。そしてこれが正常に作動するには、ループが抜けてほしいときにaが3になってくれないと困りますね。デバッグをし、aの中にしまわれた変数の値を追ってみると、n回目のループのときにaが3になるはずだったのに、実際にはn+4になって初めてaが3になるということがよくあります。この原因はそれ以前のプログラムに原因があるからですが、これはaの、そのときの値が一体いくつなのかを把握できなければ、なかなか見つけることはできません。 このように、デバッガは優れた機能を持っています。デバッガには他にも機能がありますが、これだけでも十分な威力を発揮してくれることが分かってくれると思います。もちろんデバッガがなくても想定外の動きをするエラーを潰すことはできますが、デバッガをうまく活用することで、より効率的にエラーの原因を見つけることが可能となります。デバッガをお持ちであれば、宝の持ち腐れにしないで、コンパイラ同様、使えるツールとして活用してください。 そうは言っても、デバッガは実行の動きや変数内の値を示すだけで、何が原因で思い通りに動かないのかを示してくれるわけではありません。ですので、デバッガで変な箇所を見つけたら、ソース文とにらめっこする必要があります。「ここで変な動きをしていたんだけど、どうしてそうなってしまうんだろう」「ここを直せば、正確に動くのかな?」といった感じでね。結局、エラー潰しにはソース文をよく理解し、プログラムの動きを頭の中で正確にイメージする能力が大事になってきます。そして文法のしっかりとした理解と、自分の思い描く理想の実行順序による先入観の排除、そしてソース文をよく読みこむことが大切です。 一番プログラム開発で根気が要るのがデバッグですが、ここさえクリアすれば完成です。僕もデバッグに苦しみ、何度もパソコンを叩き壊してやろうかと思いました。ですが今まで起きたエラーは全て解決することができました。どうしても無理だと感じても、寝て次の日に気持ちを変えてやってみるとできることもあります。諦めずがんばってください。諦めてしまうことは、この上なくもったいないことですから。 参考: ウィキペディア ASCIIデジタル用語辞典 コマンドプロンプトを使ってみよう! コマンドプロンプトで作業効率UP FORTRANの歴史 Fortranを使おう |