プログラミングをしている限り,エラーメッセージに遭遇するのは避けられないことだ.そこで,あなたは周りのできる人に「エラーが出ました」と言って "答え" を聞こうとするだろう.
でも,もし聞ける人が誰もいなかったら? もし,周りの誰にもわからないようなエラーにぶつかってしまったら?
あなたが一人前のプログラマになるためには,自分でエラーメッセージを読んで,解決できるようにならなければならない.
どういうエラーメッセージが出たときは何が原因で,どのように対処すれば解決するのか.その知識・経験の積み重ねこそがあなたを一人前のプログラマにするのだ.これは安直に "答え" だけを追い求めていてはいつまで経っても身に付かない.
エラーメッセージの大原則
まず最初に頭に入れておくべきなのは,エラーメッセージは意味不明な暗号ではない ということだ.その実,エラーメッセージはかなり簡単な英語で書かれている.決まった用語や,決まった形の文を使いまわしている.それらを覚えれば,未知なるエラーメッセージに遭遇したとしても,なんとか解釈することができる.エラーメッセージは読めるし,読むべきものだ.
1.動詞を含む短い文を探す
大量のエラーメッセージを前にしたプログラミング初心者は,とかくその量に圧倒されてしまいがちだ.まずは一旦落ち着いて,動詞を含む短い文 を探すとよい.
- 'i' undeclared (first use in this function)
- parse error before '}'
- undefined reference to 'prnitf'
- unterminated string or character constant
このような短い文章がエラーメッセージの本質的な部分だ(大体は undefined などの動詞から始まる).近くにエラーが発生したファイル名や行番号が表示されていることが多い.
見て分かるとおりこれらは非常に単純な英語で,ほとんどの場合10語未満だ.使われる単語もほぼ決まっているので,読めるようになるべきだ.もし読めない場合でも,Google 翻訳を使ってでも自力で読むようにしよう. 実際に Google 翻訳にかけると,
- 'i'が宣言されていません(この関数で最初に使用されます)
- '}'の前のパースエラー
- 'prnitf'への未定義の参照
- 終端されていない文字列または文字定数
となる.この時点で対処方法がはっきりわかるかもしれないし,そうでなくても,少なくともどういうことを言っているのかは理解できるはずだ.
2.ファイル名が書いてあるときは,そのファイルに原因がある
当たり前のようだが,エラーメッセージにファイル名が出てきているとき,まさにそのファイルに問題がある.行番号が表示されているならば,ほとんどの場合,まさにその行にエラーがある.
次にするべきなのは,その部分や周囲(大体は前)に間違っている部分がないかチェックすることだ.上で見つけた文と合わせれば,どう間違っててどうすれば解決するのかすぐにわかる場合が多い.
"at" や "before" といった位置を表す前置詞にも注目.関数の定義とその関数を呼び出す部分で何か食い違いがある時,"from" などの前置詞を使って呼び出し元の位置を表示していることもある.
3.長いエラーメッセージは上から順番に読む
もしエラーメッセージがとてつもなく大量に出てきたとしても慌てる必要はない.
大多数のコンパイラはソースコードを上から読む.するとソースコードの上の方で見つかったエラーほど先に報告するから,エラーメッセージの上の方に表示されることになる.
上の方にあるコードというのはそれより下のたくさんの部分で使われている(可能性がある)から,エラーが影響する範囲が大きくなる.だから,エラーは上から順番に対処したほうがよいのだ.
しかも,上の方にあるエラーが原因で,それを使う他の部分でもエラーが起こっていることもある.そのようなエラーは根本的なエラーを直したら消えてしまうことが多い.その意味でも,上の方にあるエラーを先に直すべきだ.
エラーの解決の大原則
エラーメッセージを読めたら,次は解決を試みる.エラーの原因を探るのにはいくつかのコツがある.自分でエラーを解決する経験を積み重ねて自分なりのコツを編み出すことで,プログラミングは上達していく.
1.何をすればエラーメッセージが出てくる・出なくなるのかを見極める
エラーがある箇所はなんとなく分かるけど,直し方の検討が付かない場合は,該当の部分を取り除いてエラーが出なくなるかを確かめる. それでエラーが出なくなれば詳しく調べる必要があるし,何か別のエラーが出始めた場合はそれを先に直す.もしかしたらそっちが原因かもしれない.
2.エラーが再現する小さなプログラムを作る
エラーが起きる箇所だけを取り出して,そのエラーが出るような,できるだけ小さいプログラムを新しく作ってみる. そしてそのプログラムがどのように動いているのかを確認して,何故エラーが出るのかを考えていく.
これは大きなプログラムを作っている場合は特に有効だ.一見遠回りのようだが,エラーの原因となる部分に集中することができるから,結果的にはエラーの解決が早くなる.
このような,同じエラーが出るできるだけ小さなプログラムのことを,最小再現コード (MRE) という.エラーを出すために必要な要素だけが詰まっているために詳しく調べやすいので,後述の Google 検索や質問サイトを使う時は,この MRE を使うとよい.
3.プログラムの動作の流れを追いかける
コンパイル時にエラーメッセージが出てくれるならばよいのだが,コンパイルは通ったのに実行してみるとエラーが出る,ということも多い.そもそもコンパイラがない言語ならばなおさらだ.
そういう場合は,プログラムが実際にどのように動いているのか,どのコードがどういう順番で実行されているのかを把握することで,エラーの原因を特定しやすくなる.
プログラムを動かしてみてエラーの原因(bug,バグ)を探る作業を デバッグ(debug) という.デバッグにはいくつかの手法がある.
-
printf
デバッグ- エラーの原因となっていそうな変数があるとき,
printf
などの画面に表示させる関数を使って,その変数の値を処理の途中途中で表示させる. - どこかのタイミングで変数の値が意図しないものに変わったならば,そこが原因だ.
- どこに書き加えた
printf
なのかが分かるように,出力を工夫するとよい.
- エラーの原因となっていそうな変数があるとき,
-
デバッガを使う
- 言語によっては,デバッガと呼ばれる,プログラムの実行を途中で止めたり,変数の値を覗いたりすることができるプログラムを使える.
- C言語ならば GDB,Python ならば PDB.自分の使っている言語向けのデバッガが何かは自分で調べる.
- Visual Studio などの統合開発環境(IDE)と呼ばれるエディタを使っているならば,標準でデバッガが搭載されていることが多い.
-
printf
デバッグより取っ付きにくいかもしれないが,こちらのほうが便利だ.使って慣れておこう.
大きなプログラムをデバッグするのは大変だが,小さなプログラムならば多少デバッグしやすくなる.MRE を作ると,デバッグの役に立つ.
4.仮説・予想・実験・考察のサイクルを回す
愚直にデバッグしていてもエラーの原因は見つかるかもしれないが,何故エラーが出るのかを考えながらデバッグしたほうがよほど速く見つかる.そこで,
1.エラーの原因の仮説を立てる.
2.もしその原因が正しかったと仮定して,きちんと動く場合/エラーになる場合 を予想する.
3.その場合になるように実際に動かしてみて,予想通り成功/失敗するか確かめる.
4.予想と違った場合,改めて考え直して,違う仮説を立てる.
というサイクルを回すことで,闇雲に探すよりも速く原因を特定することができる.例えば入力した数式を計算するプログラムを作っていて,計算結果が合わない場合は,
1.掛け算より足し算が先に計算されているかもしれない.
2.もしそうなら,1+2*3
は 9
になるはずだ.
3.実際に 1+2*3
を入力して計算してみる.
4.7
だった.原因は他にある.
という具合だ.
なお,やらかしそうな間違いをあらかじめ予想しておいて,実際にやらかした場合に気付けるように,簡単に実験を回せるようにしておくのが,テストを書くということである.
5.これらを組み合わせる
原則2~4はお互いを高め合う効果がある.
-
MRE を作れれば,コードが短くなってデバッグがしやすくなるし,考慮するべき物事が少なくなるので,仮説を立てやすくなる.
-
デバッグでプログラムの動作を把握すれば,仮説が立てやすくなるし,エラーと関係ない部分が分かって MRE を作りやすくなる.
-
的確な仮説を立てられるようになれば,MRE 作りやデバッグが手探り状態ではなくなる.
- 仮説を複数立てて,それぞれに対応した MRE を書いてみて,どれが本当にエラーになるのか(もしくはどれもならないのか)を調べることができる.
- そして,"当たり" の MRE をデバッグして直すことができる.
6.エラーは1つずつ解決する
一気に全部のエラーを解決しようとは絶対にしないこと. 複数のエラーを解決しようとすると MRE が作れなくなるし,考えることがあまりにも多くなって頭がパンクし,何も分からなくなる.
困難は分割せよ. 慌てる必要はない.大量のエラーメッセージも上から順番に一個一個解決していくのが一番早い.一見ひとつの大きな問題に見えることでも,実際は複数の小さな問題が同時に起こった結果だ.それを見極め,細かく切り分けて,一つ一つ対処する.
[追記] 7.はじめからやり直せるようにしておく
ファイル入出力を行うプログラムや Makefile など,ファイルの読み書きが発生するものをデバッグする時には,実行前と後で状態が変わってしまうので,実行する前の状態でディレクトリごとコピーしておいて,めちゃくちゃになっても最初からやり直せるようにしておく.
デバッグしているうちに何がどうなっているのか,自分が今何をやっているのか,さっぱりわからなくなるのはよくあることだ.そこで一旦考えをリセットするために,動いていた・分かっていた段階でバックアップを取っておこう. もちろん,"エラーは1つずつ解決する" 原則を守っていれば,そういう事態は起こりにくくなる.
なお,この原則はバージョン管理システム(git など)の考え方に直接繋がる.早めに使えるようにしたほうがよいだろう.
Google 検索の大原則
エラーを自力で解決できない時には,Google などの検索エンジンを使って調べることになる.
どんなに経験を積んだプログラマであっても,エラーメッセージが出たときに Google 検索に頼ることはある.単純に今まで見たことがなかったり,起こらないと思っていた箇所で発生していたり,そういう場合だ.
しかし経験を積んだプログラマは,当たり前だけど,Google 検索のやりかたにも経験を積んでいる.Google 検索は便利なツールだけれども,適切な検索キーワードを入れてやらないと,必要な情報にたどり着けないことが多い.彼らはそのうまいやり方を知っているのだ.
どういうキーワードで検索すれば欲しい情報が見つかるのか,その感覚を身につけることがプログラミング上達への近道だ.
1."動詞を含む短い文" をそのまま Google 検索に入れる
前述の "エラーメッセージの大原則" で探すように述べた "動詞を含む短い文" をそのまま Google 検索に入れてみるのはかなり賢い選択だ.何故なら,それがまさにエラーの本質的な部分だからだ.
こういう文は短くて特徴的で目立つから,エラーについて解説しているサイトや,そのエラーについて質問しているページなどでも,その短い文をエラーの名前として使っているものがほとんどだ.
2.エラーコードで検索する
エラーを分類するコードやIDのようなものが表示されていたら,それを検索キーワードに使うのも有効だ.例えば
error CS0246: The type or namespace name 'MyClass' could not be found.
というエラーだったら,error CS0246
で検索してみるのもよい.エラーコードは英語のプログラミング環境でも日本語のプログラミング環境でも同じものが使われるので,日本語の解説がある場合にそれが出てきやすくなる.
3.ファイル名は検索キーワードに入れない
ファイル名・ディレクトリ名はあなたが勝手に決めたものである場合がほとんどで,インターネット上の誰かが全く同じファイル名を使っている確率はゼロだ.というわけで,それらは検索キーワードに入れない.
一般に,あなたが勝手に決めた名前はなるべく検索キーワードに入れないようにするべきだ.先ほどの例で言えば,MyClass
なんて名前を付けている人が他にいて,あなたと同じエラーに遭遇している確率はゼロに近いだろう.だから,The type or namespace name 'MyClass' could not be found.
で検索するより,そういう部分を除いた The type or namespace name could not be found.
で検索したり,エラーコード CS0426
で検索したほうが良い結果が得られる.
4.情報を絞り込むキーワードを追加する
原因が違っていても同じエラーメッセージが出ることはそれなりにある.そのせいで,単純にエラーメッセージで調べても,違う状況について解説したページばかり出てきて,本当に欲しい情報が出てこないことがある.そういうときは,情報を絞り込むためにいくつかキーワードを追加する.
キーワードには,今直面している状況の(エラーが出なかった時と比べた)特殊さを端的に表現するものを使う.例えば,ソースコードファイルが1つしかなかった時は出なかったエラーが,ファイルを増やした途端出てきたような場合は,「複数ファイル」などのキーワードを追加する.ファイルを読み書きするようなプログラムを書いていて,名前が英数字なファイルは開けるのに日本語が入っていたら開けない,という状況になったならば,「ファイル名 日本語」などのキーワードを追加する.
また,そのようなキーワードを選ぶ際には,本当にそれが原因なのかどうかをあらかじめ確かめておく必要がある.もしそれが原因ではなかったならば,全く見当違いな情報が出てくることになるからだ.ソースコードファイルを増やしたらエラーが出てきたと思うなら,一旦ファイルを減らしてみてエラーが消えることを確認するべきだ.上述の MRE を作っておけば,この作業は要らなくなる.
5.英語で調べてみる
日本人は1億人ちょっとしかいないが,英語の話者は18億人もいる.当然,日本語で書かれた情報よりも,英語で書かれた情報のほうが何倍も多い.日本語で対処方法を解説したページが全く見つからないときでも,英語でならば,ほとんどの場合,何かしらの情報が見つかる.
プログラマをやっていると,どうしても英語の情報を読まなければならない状況は必ずやってくる.そういうときに,英語だから,と尻込みしていても何も解決しない.
エラーメッセージと同様に,Google 翻訳を使ってでも自力で読む習慣を付けよう.読める人に聞いているばかりでは,いつまで経っても自力で解決できるようにはならない.
6.サンプルコードを見つけても,何も考えずにコピペしないこと!!
それが何をしていて,何故動くのかを自分で考えて,それに基づいて自分のコードを書き直すこと.
何度も言っているように,他人から得た "答え" を書き写しているだけでは,いつまで経っても身に付かないし,それを自力で書けるようにはなれない.
誰かに質問するときの大原則
Google 検索で探しても欲しい情報が得られないことも時々ある.自力で解決できる見込みがどうやらなさそうな場合は,他人に質問するしかない.
しかし,本当に欲しい情報を得るためには,質問をする場所や,質問の仕方にもコツが必要だ.暇なプログラマはそれなりにいるが,どこの馬の骨とも知れないサイトに投稿された,わけのわからない質問に答えてくれるほど暇ではない.
1.プログラミング専用の質問サイトを使う
経験を積んだプログラマが生息するウェブサイトには偏りがある.彼らは大抵の場合,プログラミング専用の質問サイトしか見ていない.
プログラミング専用の質問サイトで,日本語話者向けのものをいくつか紹介する.
-
teratail【テラテイル】|ITエンジニア特化型Q&Aサイト
- 初心者でも気軽に質問できるサイト.
- 質問のテンプレートや初心者マーク機能などがあるので,初めて質問サイトを使う場合でも安心.
- ここで質問の仕方に慣れるといいだろう.上達してきて回答者側になる時も,最初はここがオススメ.
-
- 英語圏で有名な老舗プログラミング質問サイトの日本語版.
- 上級者が集まる傾向がある.ある程度プログラミングが上達してきて,それでもわからないことがある時に使うとよい.
- かなり上達してきたプログラマは,本家の英語版しか使わなくなる.一般的にレベルが高く信頼できるサイトとされているので,読むだけでも慣れておこう.
-
- 厳密には質問サイトではなく,プログラミングの知識や,問題に遭遇したときの解決方法を,記事にしたためて共有するためのサイト.
- 質問自体を投稿するというよりは,エラーの原因が分かって解決した後に,エラーを出してしまった原因と対処方法をまとめて,1つのエラー対策記事として投稿するとよい.
- 他の人から「このやり方のほうがよい」などのアドバイスがもらえることもある.
- 技術系の記事を書く練習にもなる.Qiita の記事は玉石混交なので,あなたの書いた記事のクオリティが低くてもあまり恥ずかしがることはない.しかし,他人が読んで分かりやすい文章を書けるように努力・練習するべき.
一方,"Yahoo!知恵袋" や "教えて!goo" などの一般質問サイトを使うのは本当にやめたほうがいい."教えて君" と "教えてあげる君" が跳梁跋扈していて,まずまともな回答が来ない.
プログラミング専用の質問サイトを使う場合でも,"教えて君" になってしまわないよう気を付けよう.プログラマは "教えて君" が本当に嫌いなので,専用サイトにしか生息していないのだ.
2.質問する前にまず検索する
プログラミング専用質問サイトには,毎日のようにプログラミングに関する質問が投稿されては解決されている.あなたが初心者であればあるほど,他の初心者と同じ問題にぶつかっている可能性が高い. つまり,同じような質問が既にされている可能性も高い.
Google 検索で出てこなくても,質問サイト内検索では出てくるかもしれない.質問を投稿する前に,もう一度検索してみよう. "Google 検索の大原則" はここでも活きる.
3.エラーメッセージは原文通りに貼り付けて,実行環境を詳しく書く
経験を積んだプログラマは,エラーメッセージの中からあなたが見逃した部分にしっかり気づいて,対処方法を教えてくれる.だから,エラーメッセージの一部ではなく,全部を貼り付けるようにする.
もしエラーメッセージに個人的なファイル名が含まれていてそのまま投稿するのが恥ずかしい場合は,そこだけ隠してしまっても構わない.例えば C:\Users\Tarou\練習\main.c
なら,C:\Users\XXX\XXX\main.c
のように隠す.ただし,肝心のソースコードのファイル名はなるべく隠さないこと.また,ファイル名がエラーの原因になっていそうな場合は,当然ながら隠さないほうがいい.
また,どのような環境でそのエラーが出たのかを詳しく書くことが大事だ.特定の環境でしか出ないエラーはよくあるが,他の人があなたと同じパソコンを使っている可能性はゼロだ.どういう環境でプログラミングしているのかは,書いてくれないとわからない.
-
使っている OS (Windows,Mac,etc)
- できれば OS のバージョンも書いたほうが良い.OS バージョンの調べ方が分からないなら,自分で検索する.
-
コンパイラやインタプリタのバージョン
- 例えばC言語を使っていて GCC でコンパイルしているならば,
gcc -v
というコマンドを実行すればバージョンを表示させることができる. - Python なら
python --version
.使っているプログラミング言語のバージョンの出し方が分からないならば,まずそれを検索すること. - エラーメッセージと同じで,出力をそのまま全部載せる.
- 例えばC言語を使っていて GCC でコンパイルしているならば,
-
プログラムをコンパイルしたり実行したりする時に打ったコマンド
- 他の人が質問を見て,同じようなプログラムを同じようなやり方で実行できる程度には,どういう風に実行したかを書いておくとよい.
エラーを解決するのに大事な要素に "再現性" がある.エラーに再現性があるとは,あなた以外の人も同じエラーを出せる,という意味だ.どのようにすればそのエラーを再現できるのかを他の人に的確に伝えることができれば,解決方法を探すのを手伝ってくれる.
上述の MRE を作っておくと,他の人も試しやすくなる. MRE を他人に渡す際は,数ファイル程度の小さいものは Gist などに,何十ファイルもある大きいものは GitHub などに置くとよい.特に Web 系は最小構成であっても各種 config でファイル数が多くなりがちなので,GitHub に置いたほうがよいだろう.
4.自己解決した場合はその解決方法を書き残す
ある程度プログラミングに上達してくると,質問はしたけれど誰かが答える前に自分で解決できた,ということが起こる.その時に,「解決しました」とだけ書き残して去ることだけは絶対にやってはいけない.
どのようにすれば解決するのかをきちんと書き残すことで,また同じエラーが出た時にそこを見ればよくなるし,同じようなエラーが出た他の人の助けになる.
5.複数の質問サイトに同じ質問を投稿しない
上と似たような理由で,複数の質問サイトに同じ質問を投稿するのもやめたほうがいい.
回答が付かなかったサイトにも解決方法を書き残す必要が出てきて面倒だし,かといってそれをしなければ,回答のない質問が検索に出てくることになって,解決方法が載っているものにたどり着きにくくなる.だから最初から1つの質問サイトで質問したほうがいい.
もしいくら待っても全然回答が付かなくて,他のサイトで聞いたほうが早そうな場合は,あくまでも最後の手段として,「あのサイトに投稿したこういう質問に答えてくれませんか?」という質問を(もちろん対象の質問の URL 付きで)投稿するとよい.
6.答えてくれた人に感謝の気持ちを伝える
当たり前だけど,答えてもらってお礼を言わないのは非常に印象が悪い.一言でいいので感謝のコメントを付けておこう.
7.サンプルコードをもらっても,何も考えずにコピペしないこと!!
もう説明は要らないはず.
その他
-
日本語と英語の能力は,時としてプログラミング言語の能力よりも重要になる.
-
プログラムは思ったとおりには動いてくれないかもしれないが,少なくとも書いたとおりには動いている. 自分が今何を書いているのかを,できるだけ把握する.
-
人に教えることが一番勉強になる. 自分が理解していることを,他人にわかるようにきちんと文章に起こすことで,あまりよく分かっていない部分に気づいたり,感覚でやっていたことが明文化されたりして,自分の理解が一層深まるからだ.
- プログラミングが上達してきたら,今度は質問サイトで答える側に回ってみよう.
- 教える側と教えられる側に,上下関係・偉い偉くないは(本質的には)ない.教える側のほうがより難しく厳しい勉強をしているだけのこと.お互いにきちんとリスペクトしあい,自分の立場に責任を持つこと.
-
"自力で考える・自力でやってみる" 姿勢が一番大事.
- 自力で何もしなくなったプログラマの成長は,残酷なほどすぐに止まる.
参考リンク
-
ハッカーになろう (How To Become A Hacker) - Eric S.Raymond
- 著名なハッカー(優秀なプログラマの意)の Eric S. Raymond が,ハッカーとしての心構えや行動指針を語る.
- 優秀なプログラマは多かれ少なかれ,似たような思想を持って,似たようなことをしている.参考・目標にしよう.
-
プログラマの心の健康 - 結城浩
- 「数学ガール」シリーズで有名なプログラマ・作家の結城浩氏からのエール.
- バグがどうしても取れなくてイライラする時,なにもかもうまく行かない時,自分はプログラミングに向いてないと思う時,そういう時に読みましょう.わたしも,結城氏みたいなすごい人も,みんなそういう経験をしています.
-
お前は絶望的にプログラミングに向いてないから諦めて刺身にタンポポ乗せる仕事でもやってろ - 古都こと [リンク切れ]
- この人はめちゃくちゃに口が悪いのでよろしくないのだが,言っている内容自体はとても良い.
- 「公式ドキュメント読めば5分で解決することで5時間もGoogleとにらめっこするな」[リンク切れ] など,他にもためになる話が多い.
- この文章にたどり着いている時点であなたはそんなにダメじゃないです,安心しましょう.落ち込まないで.でも怠慢はダメです.
- なにくそ,こんなやつ,絶対見返してやるぞ,となる方はそのように.
-
教えてクン養成マニュアル
- Don't be that guy.(あんなやつになるなよ.)
[追記] 書くときに参考にした記事
書くのを忘れていた! F# を知ってほしい と同じで自分が誰かに教える時にリンクを投げつければ済むようにしたかっただけで,あまり多くの人に読まれるのは想定してなかった.
自分が普段感覚的にやっている動きを,客観的に捉えて明文化し(ようとし)たのがこの記事です.以下はその助けになった記事です.
-
gcc のエラーメッセージの読み方
- 最初の文が特に秀逸.他にもよい文が多く,パクリみたいになってしまった.
- エラーメッセージ例はこちらから取らせていただきました.
- わたしの記事の "読み方" パートはこの記事のおかげです.
-
デバッグの手法 [リンク切れ]
- 同じく,わたしの記事の "対処" パートはこの記事のおかげです.
- そう,ある程度上達してくると "分割統治法" で十分になります.しかし分割統治法の感覚を身につけて頭の中だけで完結できるようになるまでは,MRE を手を動かして作ったほうがよいと思われます.
- 上の記事もそうですが,大学の先生の書く文章というのはなぜいつもこんなに的確で,感覚として済まされがちなものをうまく明文化しているのでしょうか.
記事の書き方自体について参考にした記事です.
-
はてブの主要客層である読解力皆無バカへの対策を文章に施すためには - 古都こと [リンク切れ]
- 誰にでもわかりやすい文章を書く際のコツの解説記事として.
- この方は本当に口が悪い…… でも,そこもまた魅力の一つなのだろう.読んでて痛快なのはそうですね.
これらに加えて,参考リンク先を参考にしています.