このページでは、プログラムがうまく動作してくれなくて困った時に「やるべきこと」について解説していきます。
まず「プログラムがうまく動作してくれない」という経験は、プログラマーであれば誰でも経験することです。ですので、自分が作成したプログラムがうまく動作してくれないからといって、あなたがプログラマーに向いていないとか、才能がないとか、そういう事ではないので安心してください。
むしろ、これはあなたがプログラミングの力を伸ばすためのチャンスです。プログラムがうまく動作してくれない際に原因を調査し、それを解決していくことでプログラミングの力を大きく伸ばしていくことができます。
ただし、ボーッとソースコードを眺めているだけだと原因の特定が難しい場合も多いです。
では、プログラムがうまく動かない場合は何をして解決していけば良いのか?
この点について、特にプログラミング初心者の方向けに解説していきたいと思います。
Contents
エラーや警告が出ていないかを確認する
プログラムが上手く動かない場合には、まずエラーや警告が出ていないかどうかを確認しましょう。
- プログラム実行時にエラーが出ていないか?
- コンパイル時に警告やエラーが出ていないか?
もし出ている場合はラッキーで、これらのエラーや警告を直すだけでプログラムが上手く動作するようになる可能性があります。
コンパイル時のエラーや警告に関しては、エラーや警告の文言をそのまま Google 等で検索すれば大体原因や対処法が確認できると思います。
プログラム実行時のエラーに関しても、そのエラーの文言をそのまま Google 等で検索するのが基本だと思います。ですが、もしライブラリや外部モジュールが出力しているエラーであれば、そのライブラリや外部モジュール名も検索キーワードに含めた方が、期待するページが見つかりやすいと思います。
出力されているエラーが、あなたが print
関数などで出力しているものであれば、そのエラー文は一般的なものではないので検索しても対処法が見つかる可能性は非常に低いです
この場合は、まず “なぜそのエラーが出力されているのか” を考える必要があります
各処理に対して期待する動作を考える
厄介なのは、エラーや警告が出ていないのに上手く動作してくれない場合です。
様々な処理が並列して同時に実行されるプログラムやメモリ破壊などが起こっている場合は更に多くの可能性を考慮する必要がありますが、特にプログラミング初心者の方がまず確認すべきことは下記の4つだと思います。
- 各処理が期待した通りに動作しているかどうか?
- 各処理に期待するデータが入力できているかどうか?
- 実行されることを期待している処理が本当に実行されているかどうか?
- “プログラムでやりたいこと” が記述した処理のみで実現できるのか?
“処理” とはソースコードの1行や演算・関数実行あたりを考えていただければ具体的にイメージしやすいのではないかと思います。
あなたが作成したソースコードは、あなたが期待した通りにプログラムが動作するよう、コンピュータ(CPU)に実行してもらいたい処理を記述していると思います。さらに、あなたが記述した1つ1つの処理には期待する動作があるはずです。もし期待する動作がないのであれば、その処理は無駄な処理ということになります。
ただ、プログラムが上手く動かないということは、その処理のいずれか、もしくは複数のものが、あなたが期待した通りの動作になっていないということになります。
なので、まずはその期待した通りの動作になっていない処理を特定し、その処理が期待した通りの動作になるように修正してやる必要があります。
ただ、期待した通りに動作になっているかどうかを判断するためには、まずあなた自身が各処理にどのような動作を期待しているかを理解しておく必要があります。
もちろん、あなた自身がプログラミングしているのであれば、各処理に期待する動作を考えながらプログラミングしているはずなので各処理の期待する動作を改めて考える必要もないと思います。
それに対して、コピペしてきたソースコードに関しては注意が必要です。
コピペしてきたソースコードにおいては、あなたが各処理に期待されている動作を理解できていない可能性があります。この場合、プログラムが上手く動かない原因を調査するのが難しいです。これは前述の通り、期待した通りの動作になっているかどうかは「期待する動作」を理解していないと判断できないからです。
なので、コピペしてきたソースコードを変更してプログラムが上手く動作しなくなってしまった場合、その原因の調査の難易度は高くなります。
このようなプログラムが上手く動作しない場合は、まずはコピペしてきたソースコードに記述されている各処理に期待する動作を整理してから原因調査をした方が良いです。
スポンサーリンク
各処理が期待した通りに動作しているかを確認する
各処理に期待する動作が整理できたら、次は実際に各処理が期待する動作になっているかどうかの確認をしていきます。
もし期待する動作になっていない処理があった場合、その処理が「プログラムが上手く動かない原因」である可能性が高く、まずはその処理を修正していく必要があります。
必要な情報の出力を行う
ただし、各処理であなたが期待する動作になっているかどうかを確認するためには情報が必要です。
その情報とはつまり、各処理の実行結果および、各処理に対する入力データの情報になります。
これらの情報と、各処理で期待する動作を照らし合わせることで、各処理が期待する動作になっているかどうかを判断することができるようになります。
この情報の取得は、print
関数などで各処理の実行結果や各処理に対する入力データを出力するのが一番単純な方法だと思います。
例えば下記のようなプログラムについて考えてみましょう。下記は上手く動作しないプログラムの例であり、価格が a
の商品と価格が b
の商品を 20
個ずつ購入した時に必要な代金を算出するものになります。ただし、必要な代金には消費税が 10
% がかかるものとします。
sum = a + b * 20
price = sum * 110
上手く動作しない原因は一目瞭然かもしれませんが、ここでは一行一行期待する処理になっているかどうかを確認していきたいと思います。
まず1行目は「a
と b
の和を 20
倍した結果を sum
に格納すること」を期待する処理となります。また2行目は「sum
を税込にした結果を price
に格納すること」を期待する処理となります。
さらに、これらの処理において、各処理の実行結果および、各処理に対する入力データはそれぞれ下記の変数に格納されることになります。
- 1行目
- 実行結果:
sum
- 入力データ:
a
とb
- 実行結果:
- 2行目
- 実行結果:
price
- 入力データ:
sum
- 実行結果:
これらの情報を整理した上で、各行が期待する動作になっているかを確認していきます。
具体的には、これらの入力データと実行結果の値を実際に print
関数等で表示し、その表示結果を確認することで、1行目と2行目がそれぞれ期待した通りに動作しているかを判断していきます。
上記のソースコードに対し、必要な情報を出力するように変更を加えたものが下記となります(表示を行う際に利用する関数は言語によって異なるので注意してください)。
print(a)
print(b)
sum = a + b * 20
print(sum)
price = sum * 110
print(price)
例えば a
と b
がそれぞれ 100
と 50
であるならば、print
での表示結果は下記のようになります。
100 50 1100 121000
出力された情報から期待した動作をしていない処理を見つけ出す
まず1行目の処理に期待する動作は「a
と b
の和を 20
倍した結果を sum
に格納する」です。a
と b
がそれぞれ 100
と 50
であるのであれば、sum
は 3000
になることが期待されます。
それに対して上記では sum
が 1100
になっています。
すなわち、1行目は期待した動作が行われていない処理になります。期待した動作が行われない処理があるとプログラム全体としても上手く動作しないため、まずここを修正する必要があります。
また、2行目の処理に期待する動作は「sum
を税込にした結果を price
に格納する」です。消費税は 10
% ですので、sum
が 1100
であるのであれば、price
は 1210
になることが期待されます。
それに対して上記では price
が 121000
になっています。
すなわち、2行目は期待した動作が行われていない処理になります。期待した動作が行われない処理があるとプログラム全体としても上手く動作しないため、ここも修正する必要があります。
上記の例がすごく簡単なものなので、プログラムが上手く動かない原因を掴めるのが当たり前のように感じるかも知れませんが、上手く動かない場合の原因調査の基本はコレです。
つまり、期待している動作をしているかどうかを確認するための情報を出力するようにし、さらに出力された情報から期待している動作が本当に行われているかを確認していくことで、プログラムが上手く動作しない原因を解明していくことになります。
情報の出力は期待する動作に合わせて変更する
ちなみに、必要な情報の出力方法は print
等での文字列表示のみとは限りません。
例えばプログラムの途中で画像処理を行うようなプログラムの場合、その画像処理が期待した通りに動作しているかどうかに関しては、画像処理前後の画像をファイルとして保存し、そのファイルをプレビューアプリなどで開いて画像比較した方が確認が楽です。
期待する動作になっているかどうかが確認しやすいように、適切に情報を出力する方法を選択する必要があります。
実行結果だけでなく入力データの確認もした方が良い
また、ある処理の実行結果がおかしい場合、その処理ではなく入力データの方に原因がある場合もあります。
ですので、入力データに関しても期待するものになっているかどうかの確認を行い、入力データがおかしい場合は、その入力データを生成する処理の方に問題がないかどうかを確認するようにしましょう。
処理が実行されているかどうかの確認も必要
さらに、実行されることを期待している処理が実際には実行されておらず、それが原因でプログラムが上手く動作しないケースもあり得ます。
例えば、ある関数が呼び出されて実行されることを期待していたとしても、間違って他の関数が呼び出されたりしていて実際には呼び出されていないような場合もあります。
この、処理の実行の有無に関しても、print
等を利用すれば確認することができます。
例えば特定の関数が本当に実行されているかどうかは、その関数の先頭付近や関数の最後付近に print
を仕込んでおけば、関数が実際に実行されたかどうかをプログラムの表示結果から確認することができます。
def functionA():
print("enter functionA")
# 関数の処理
print("leave functionA")
こんな感じで、プログラムが上手く動作しない場合は下記が確認できるように情報を出力するようにし、さらにその出力結果と期待する動作を照らし合わせながらプログラムが上手く動作しない原因を追求していくのがオーソドックスなやり方だと思います。
- 各処理が期待した通りに動作しているかどうか?
- 各処理に期待するデータが入力できているかどうか?
- 実行されることを期待している処理が本当に実行されているかどうか?
必要な処理が抜けていないかも考えながら確認
もし、各処理が期待した通りに動作しているのにプログラムの実行結果が期待したものでない場合、そもそもプログラムでやりたいことを実現するのに処理が足りていないことになります。
この場合は、プログラムでやりたいことを実現するために必要な処理に対し、あなたがソースコードに記述した処理に不足しているものがないかどうかを確認するようにしましょう。
各処理に期待する動作を考え、さらに各処理の実行結果や入力データを確認していくことで、不足する処理にも気付きやすくなると思います。
段階的に原因の切り分けを行なっていく
ただ、全ての処理に対して期待した通りに動作しているかどうかを確認していくのは凄く大変です。全ての行の前後に print
等を仕込むようなものなので、特にソースコードが長くなると大変になります。
なので、もう少し大雑把にソースコードを分割して考え、段階的に原因の切り分けを行なっていく方が良いです。
例えば下記のような流れで動作していくプログラムのソースコードについて考えていきましょう。
- 画像ファイルを読み込む
- 読み込んだ画像をリサイズする
- リサイズした画像を回転する
- 回転した画像をグレースケールに変換する
- グレースケール変換後の画像をファイル保存する
例えばソースコードの行数が200行などあれば、全ての処理を1つ1つ期待した動作になっているかを確認するのは結構大変です。
なので、機能単位でプログラムが上手く動作しない原因を確認していく方が効率が良いです。
具体的には、上記の機能を1つ行うたびに画像ファイルの保存を行うようにすれば、期待した動作になっていない機能を特定することができます。
- 画像ファイルを読み込む
- 最後に画像をファイル保存する処理を追加する
- 読み込んだ画像をリサイズする
- 最後に画像をファイル保存する処理を追加する
- リサイズした画像を回転する
- 最後に画像をファイル保存する処理を追加する
- 回転した画像をグレースケールに変換する
- 最後に画像をファイル保存する処理を追加する
- グレースケール変換後の画像をファイル保存する
例えば、リサイズ後に保存した画像の結果が期待した通りのものになっているのに、回転後に保存した画像の結果が期待したものになっていない場合、「画像ファイルを読み込む」「読み込んだ画像をリサイズする」機能には問題がなく、「リサイズした画像を回転する」機能に問題があると考えることができ、プログラムが上手く動作しない原因の調査を行う必要のある範囲を一段階狭めることができます。
あとは「リサイズした画像を回転する」機能が上手く動作しない原因を突き止めていけば良いだけなので、プログラム全体から原因を突き止めていくよりも楽になります。こんな感じで、プログラムが上手く動作しない原因となる処理が存在する範囲を段階的に狭めながら切り分けていくことで、効率的に最終的な原因を見つけることができるようになります。
また、上記では機能ごとに分割して原因の切り分けを行いましたが、機能ごとに分割することに拘る必要はないです。大事なのは、分割した単位ごとに「期待する動作」をあなた自身でハッキリと理解できるように分割することです。
期待する動作が分からなければ、実行結果や入力データを出力して確認したところで期待する動作になっているかどうかの判断を行うことができません。
スポンサーリンク
原因が特定できたら修正する
期待する動作になっていない処理を見つけられたら、後はその処理が期待する動作になるように修正しましょう!
どの処理に問題があるかが分かっている&期待する動作が分かっているはずなので、おそらく修正は簡単だと思います。
ちょっと難しいのが、その処理が “ライブラリの関数の実行” である場合です。この場合は、その関数に渡す引数がおかしい場合もありますし、実行するための前準備などが必要である場合もあります。
こういった場合は、期待する動作をしてくれない “関数(メソッド)の名前” や “ライブラリ名”、さらには “使い方” などをキーワードに含めてウェブ検索を行い、再度使い方が正しいかどうかを確認するのが良いと思います(もちろんライブラリの公式ドキュメントを読むのでも良いです)。
例えば OpenCV2
というライブラリの resize
というメソッドが期待する動作をしてくれないと分かった場合は、「OpenCV2 resize 使い方」などで検索して見つかったページから resize
メソッドの使い方が間違っていないかどうかを確認します。
また、プログラムが上手く動作しない原因は1つだけとは限りません。1つの処理を修正してもまだ上手く動作してくれない場合は、他に期待する動作になっていない処理がないかを確認し、期待する動作になっていない処理全てを修正していく必要があります。
「プログラムが上手く動かない…」で困らないためのポイント
プログラムが上手く動かない現象は、特にプログラミング初心者の方にとって辛い現象だと思います。
作成したプログラムが上手く動いてくれなくてプログラミングに挫折した人も多いと思いますし、修正が大変でプログラム作成を途中で投げ出した人も多いと思います。
最後に、そういったことにならないようにするために心がけた方が良いポイントについて解説していきたいと思います。
できるだけ小さな単位で動作確認する
まず大事なのが、できるだけ小さな単位で動作確認することです。
一気に大量のソースコードを書いてから動作確認した場合、プログラムが上手く動いてくれないことの方が多いです。
さらに、ソースコードが長い分、上手く動かない原因が多く存在する可能性も高くなり、プログラムが上手く動かない原因全てを特定するのが大変になります。
このような場合、直しても直してもプログラムが上手く動作してくれなくて、プログラミングを投げ出したくなることも多いです。
ちょっと面倒にも感じるかもしれないですが、できるだけ小さい単位で細かく動作確認を行い、その小さい単位の動作確認が終わった後に、次に必要な処理を記述していくようにした方が良いです。
例えば、「区切りの良い箇所までソースコードを書いたら動作確認する」「関数1つ書いたらその関数の動作確認をする」を繰り返しながらソースコードを書いていく方が良いです。
特に、適切に処理を関数に切り出していくことで動作確認がやりやすくなると思います。関数には必ずハッキリとした期待する動作があるはずで、期待する動作になっているかどうかも入力データ(引数など)と実行結果(返却値など)から確認しやすいです。
動作確認を繰り返しながらソースコードを完成させていく必要があるので時間がかかりそうにも思えそうですが、実は最終的にプログラムが出来上がるのが早かったりもします。
スポンサーリンク
デバッガーを利用して楽に原因調査できるようにする
また、ここまで期待する動作になっているかを確認するために主に print
等の文字列表示関数を利用してきましたが、デバッガーを利用すればそういった関数を利用することなく、処理が期待する動作になっているかどうかを確認することができます。
デバッガーとはデバッグをサポートしてくれるツールであり、要はプログラムが上手く動作しない原因の特定を助けてくれるツールになります。
このデバッガーには下記のような機能を持つものが多いです。
- プログラムを特定の行で止める
- プログラムを止めたタイミングでの各変数の値を表示する
- プログラムを一行ずつ止めながら実行する
私が VSCode を利用しているので VSCode 上でのデバッガーの利用例を紹介していきたいと思います。
まず、下の画面のようにソースコードの特定の行にブレークポイント(赤丸)を設定すると、プログラム実行時にその行が実行される直前でプログラムを停止させることができます。
実際にプログラム実行後にブレークポイントでプログラムが停止した様子が下の画面のようになります。黄色背景の行が実行される直前で、プログラムが停止しています。
注目していただきたいのが左側のウィンドウです。このウィンドウには、プログラムが停止した時点での各変数の値が表示されています。
つまり、わざわざソースコードに print
関数等を埋め込まなくても、デバッガーさえ使えばソースコードを変更することなく変数の値を確認することが可能です。
さらに、デバッガーでは一行ずつプログラムを停止させながら実行することも可能です。例えば上の画面から一行だけプログラムを進めた時の画面は次のようになります。
プログラムが5行目の直前から6行目の直前に一行だけ進み、さらに5行目の実行結果となる sum
の値と入力データ a
と b
の値が左側のウィンドウに表示されていることが確認できると思います。
ここで5行目の処理に期待する動作を考え、もし期待する動作が「a
と b
の和を 20
倍した結果を sum
に格納する」なのであれば、左側のウィンドウに表示されている変数の値から、5行目は期待する動作をしていることが確認できますね!
同様にして、さらに一行進めれば6行目の処理が期待する動作になっているかを確認することもできます。
このように、一行ずつ停止させながらプログラムが実行でき、さらに変数の中身も確認できるため、各行が期待した動作になっているかどうかが簡単に確認できます。
また、実行されることを期待している処理が本当に実行されているかどうかもデバッガーを利用することで簡単に確認することが可能です。その処理の行にブレークポイントを設定してプログラムを実行し、その行でプログラムが停止すれば、その処理が実行されていることが確認できますし、停止しなければその処理が実行されていないことになります。
こんな感じで、デバッガーを利用することで、お手軽にプログラムが上手く動作してくれない原因を調査することができます。
プログラミングを続けていく限り、この「プログラムが上手く動作してくれない」現象と常に向き合っていく必要があり、その度に原因調査をしていく必要があります。
そして、この原因調査が大変であれば大変であるほど、おそらくプログラミングするのが億劫になると思いますし、プログラミングに挫折する可能性も高くなると思います。
なので、プログラムが上手く動作してくれない原因の調査は出来るだけお手軽に行えた方が良いです。当然その方が効率的にプログラム開発を行なっていけますし、プログラミング学習に対するモチベーションを下げないためにも重要です。
そして、そのお手軽に原因調査を行うための手段の1つがデバッガーの導入になります。今後もプログラミングを続けていくのであれば、必ずデバッガーを導入しておいた方が良いと思います。
ちなみに VSCode で C言語 や Python のプログラムをデバッガーで実行するための手順については下記のページで紹介していますので、デバッガー導入を考えている人は是非読んでみてください(これらの VSCode やデバッガーは無料で使用できます!)。
VSCodeでMacOSにC言語デバッグ環境を構築 Visual Studio Code で Mac に Python 開発環境を整える他の言語でのデバッガーの導入方法についてもウェブ上で検索することで簡単に解説ページを見つけることができると思います。
また、VSCode のプラグインの中にはスペルミスなどがあれば警告してくれるもの等もあり、デバッガー以外のプラグインも導入することで、より効率的にプログラミングを学んでいくことができます。
VSCode に限らず、プログラミング学習を効率的に進めていくためのツールや開発環境はたくさん存在しますので、どんどんそういったものも取り入れて快適にプログラミング学習を進められるようにしていけるよう、開発環境の構築にも力を入れていきましょう!
まとめ
このページでは、プログラムが上手く動作してくれなくて困った時に「やるべきこと」について解説しました!
エラーや警告が出ている場合は、まずはそれの修正を行いましょう!
エラーや警告が出ていないのにプログラムが上手く動作しないのであれば、各処理が本当にあなたが期待する通りに動作していくれているかを確認していきましょう!
この確認を行うためには、各処理に期待する動作を理解しておくことと、それを確認するための情報が必要です。
特にコピペしてきたソースコードの場合、まずはコピペしてきた処理に期待される動作が何なのかを考える必要があります。
また、情報は print
関数などでも得られますが、デバッガーを利用したほうがソースコードの変更も不要で楽です。
プログラミングを続けていく限り、プログラムが上手く動かない原因の調査は何度も行う必要があります。できるだけ原因調査をお気軽に行えるよう、デバッガーの導入をオススメします。
最初はプログラムが上手く動かないと絶望を感じることもありますが、慣れてくるとむしろその原因調査が楽しくなってきます!さらに、経験を積めば積むほど勘も働くようになり、原因調査の効率も上がります。
何より、こういった原因調査を繰り返すことで、あなたのプログラミングの力も伸ばすことができます。是非ポジティブな気持ちで、プログラムが上手く動かない原因の調査に取り組んで行ってください!