新大学生のみなさま、まずは合格おめでとうございます。大学での新生活は新しい喜びと学びに満ちています。大いに楽しんでください。

さて、運良く(運悪く)情報科に、あるいはそれに類する学科に入ることになった方は、「プログラミング実習(演習)」という必修科目を受けることになると思います。必修科目のわりにこれがなかなか難しく、多くの学生が苦戦している様子を毎年見かけます。私もプログラミングの授業は何度かTA(ティーチングアシスタント)を担当して、様々な苦難を目にしました。

そこで、プログラミング実習を乗り切るためのいくつかのアドバイスを、この記事に記したいと思います。この記事が新入生の助けになれば幸いです。

プログラミング実習という授業について

学校にもよると思いますが、プログラミングの授業は、大まかには以下のような流れになると思います:

  1. PowerPointなどを用いて、今回学ぶ要素について先生が解説する
  2. 教科書などを参考にしつつ、授業時間内に練習問題を解いて提出する
  3. 次週までに宿題問題を解いて提出する

授業名に実習(演習)と名前がつくだけあって、自分でプログラムを書くことが非常に大きな割合を占めています。授業の自由度は高く、友達と相談したり、机間巡視をしている先生やTAに質問したり、気楽な姿勢で挑むことができる授業になっているかと思われます。

しかしそれでも挫折する人が後を絶たないというのが実情です。TAとしては、もっと気楽に質問していただき、研究室にも質問に訪れてほしいのですが、それはなかなかハードルが高く難しいようです。

使用するプログラミング言語はC言語やJava、あるいはRubyやPythonなど、多岐に渡ると思います。ここではC言語を前提として話を進めてきますが、他の言語でも通用する、一般的な話をできるだけメインにしていきます。

実習において気をつけたいこと

積極的に質問する

プログラミングは、非日常的な活動です。新しい発見に満ち溢れていますが、その分、わからないことも多く存在します。そういったときに、「なぜこうなっているのだろう」「どうしてこれで動かないのだろう」と自分で考えるというのは非常に大事なことです。

ですが、それが10分や20分かかるとなると話は別です。時間は有限ですし、疑問は無限に湧いてきます。そして自分だけで考えると間違った答えにたどり着くかもしれません。自分で考える、というのは時に有害となることがあります。

そして、プログラミングは積み重ねの技術です。授業のはじめの方で抱いた疑問をそのままにしていると、大学4年の終わりまで引きずることになります。「時間が経てば別の分野に行くからリセットされる」ということがありません。わからないことをわからないまま放置していると、だんだん積み上がってきて、最後には崩壊してしまいます。

なので、少し自分で考えて、わからないときはすぐに先生やTAに質問してみましょう。あまり本質的でない、素朴な疑問でも大丈夫です。例えば「forループで使う変数の名前は、なんで”i”なんですか?」などでもかまいません。とにかく頭に浮かんだ疑問は片っ端から解決していくことにしましょう。

本やネットで調べる

授業の指定教科書は軽視されがちですが、意外と重要なことが書いてあったりします。プログラミングの入門書は玉石混交で、中にはひどいものもありますが、授業の教科書として指定されているなら、そこまでひどいことは書いていないはずです。一度、教科書にも目を通してみましょう。

複数の参考書を利用するのも手です。ひとつの本だと多面的な視点が得られず、理解に苦労することがありますが、複数の参考書を見比べて、いろんな面から見てみると、案外簡単に理解できたりするものです。なお、参考書はわざわざ買わずとも、大学の図書館にたくさん置いてあるはずです。行き詰ったときには、図書館で借りてみましょう。

またネットで調べるという方法もあります。ネットには様々な情報が転がっていますし、質問サイトもあります。例えばプログラミング質問サイトでは、Stack Overflow(http://stackoverflow.com/)が有名です。どうしてもTAなどに相談しにくい、または先生やTAの知識レベルが信用できないとなったら、質問サイトを活用してみるのも手です。

プログラミングにおいて気をつけたいこと

エラーメッセージを読む

プログラミングにおいて困ることは、大きくふたつに分けられます。「思い通りに動かない」と「そもそも動作すらしない」です。慣れてくると「思い通りに動かない」に苦しめられることになるのですが、慣れるまでは「そもそも動作すらしない」に苦しめられることの方が多いと思います。

そもそも動作すらしない場合、画面にエラーメッセージが表示されます。エラーメッセージは、なぜ動かないかの答えそのものであることが多いです。しかしそれにもよらずエラーメッセージを全く読まない人というのは非常に多く存在します。

エラーメッセージを読むことで、初歩的なミスは簡単に解決することができます。単純な例を見てみましょう。

以下のプログラムは正しくコンパイルされません:

このプログラムは、aという変数の値と、bという変数の値を表示しようとするものです。

このときコンパイルメッセージは、例えばgccを使用した場合、以下のようになります:

英語のメッセージですが、頑張って読んでみましょう。

エラーは6行目27文字目における変数bが未宣言だと言っています。つまり「int b;」のような宣言がないと、このプログラムは動かないと言っているのです。もう一度プログラムを眺めてみると、たしかに変数bに関する宣言はありません。これが動かない原因のようです。プログラムを修正してみましょう。

これでコンパイルに通り、動作するようになりました!

しかしこういった宣言ミスはなかなか起こりません。未宣言エラーは実際には以下のようなケースが多いと思われます:

このプログラムにおいても同様のエラーが発生します。

6行目18文字目で未宣言エラーが発生しています。この原因は非常に単純で、単なるタイプミスです。user_name変数を使用しようとしているのに、user_nama変数を表示しようとして、エラーになっています。

これはなかなか気づきません!しかしエラーメッセージをしっかり見ていれば、user_name変数を使用しているつもりなのに、変数が存在しないとエラーが表示される、つまりタイプミスをしている可能性があるのでは、と気づくことができます。

他の例も見てみましょう。例えば以下のようなプログラムがあるとします:

このときgccのコンパイルでは以下のようなエラーが出ます:

これを訳してみます。

今回はエラーがちょっと複雑ですね。なにやら「暗黙的」にprintf関数を使用していると警告されているようです。暗黙的ってなんだろう?なぜ?

エラーには専門的な言葉がたくさん出てくるので、全部を読む必要はありません。とりあえずprintf関数になにかがあるとわかれば、それで大丈夫です。……よくみてみるとprintfがpirntfとタイプミスされていて、そんな関数は存在しないと怒られているようです。これも簡単に修正することができますね。

次に以下のようなプログラムとエラーを見てみます:

これは8行目に’}’ではなく、宣言(declaration)か式(statement)が期待されているというエラーです。こういった場合には大抵字面通りに捉えると混乱します。‘{‘‘}’などの括弧類が絡んでいるときはたいていの場合、文の開き忘れ・閉じ忘れです。この場合はプログラムをよく読むとfor文を閉じ忘れていることに気がつきます。

このように、エラーメッセージをよく読むことで、どこにミスがあるのか、どんなミスをしているのか、ある程度予測がつくようになります。難しい言葉もたくさん出てくるので、エラーメッセージ全部を読む必要はありませんが、どこでエラーが起きているのかぐらいは見ておくと、楽になります。

プログラムを読みやすくする

膨大な数のエラーを乗り切って、ついにプログラムが動き出します!しかし今度は思った通りに動作しないのです。出力される値はぐちゃぐちゃで、全くのデタラメです。なぜこうなってしまうのでしょうか。どうすればいいのでしょうか。

意図しないプログラムの動作のことをバグといいます。バグは我々を苦しめる最大の要素で、プロのプログラマですら避けては通れぬ道なのです。しかしどうやってバグと戦っていけばいいのでしょうか。

ひとつの効果的な戦法としては、わかりやすいプログラムを書くという方法があります。プログラムは機械が読むだけではなく、人間が読むものでもあります。わかりやすいプログラムを書けば、それだけ人間のミスを減らし、バグを減らすことにつながります。

プログラムというのは書きながら何度も読みますし、将来的に昔書いたプログラムを読み返すこともあります。そのときに読みやすく書けていれば理解の助けになりますし、バグにも気づきやすくなります。どうすればいいのかわからなくなったときは、とにかく「読みやすい」「綺麗な」プログラムを目指せばいいでしょう。そうすれば、きっとバグの原因も見つかります。

わかりやすい変数名をつける

読みやすいプログラムを書く方法の一つとして、変数にわかりやすい名前をつけるというものがあります。a, b, cやx, y, zなど、短くわかりにくい変数名をつける人が多いですが、もう少しわかりやすい名前にすると、プログラムを読みやすくなります。

簡単な例を挙げてみましょう。以下のプログラムはBMIを計算するプログラムです:

恐ろしいことに、この短いプログラムの中にすでにバグがあります!期待する出力は19.5ですが、実際に出力されるのは0.00195になります。いったいなぜでしょう。

これはプログラムを眺めていても解決しません。原因はもっと根源的な部分にあります。そもそもこのプログラムは、なんの説明もなしにいきなり出されると、なにをしているのかさっぱりわかりません。つまりこれはわかりにくい、悪いプログラムということになります。

これを書き換えてみましょう。変数の名前を変えて、以下のようにしてみます:

これは変数の名前を書き換えただけです。しかしよくみてみると……ああ!身長160メートル!そうです、BMIの計算では、身長はメートルを使うことになっています。つまり、身長の160.0の部分を、1.6に書き換えることで、正しい出力を得ることができます。

このようにして、変数名にしっかりした名前をつけることで、バグを未然に発見することが楽になります。特に古い教科書においては、変数名はaやb、xやyなどといった短いものが使用されることが多いですが、現代では特に短くする必要はありません。どんどん長い変数名をつけていきましょう。

変数名を工夫するコツは、必要な情報を名前に加えることです。例えば上の例では「単位」が重要な要素となりました。単位は間違って入力しやすいので、変数名に単位の情報を加えてやると安心です。

ループに使う変数名も同じです。例えば行列の要素を表示する、以下のようなプログラムがあるとします:

これもバグを含んでいます。実行すると、Segmentation faultと表示されます。これは内側のループでj++とするところをi++としているためです。ループを扱う時に、変数名に「i」や「j」を使うのはいいのですが、1文字だけだと、どうしても間違いやすくなります。もっと長い変数名をつけてみましょう。こういった行列の操作のときには、「row(行)」や「column(列)」といった変数名にするといいでしょう。

これでプログラムがわかりやすくなり、バグも発見しやすくなります。内側のループでrow++としていたら明らかに間違いだとわかるでしょう!

コメントを入れる

変数を工夫することでプログラムの流れをわかりやすくすることができます。しかし変数名だけでは限界があります。そこでコメントの出番です。コメントを使うことで、プログラムに関係のないメモを書くことができます。

コメントは特にプログラムが大規模になってきたときに有効です。プログラムの処理の流れを読むとき、5行程度のプログラムなら、なんとか大まかな流れは読めますが、20行などになってくると、なかなか難しくなります。そんなときにコメントがあると、とても読みやすくなります。

実習の授業ではコメントを書かないという人が大多数ですが、できればコメントを書く癖をつけておきましょう。特にプログラミングに触れたてのときは、わかりやすい、綺麗なプログラムを書くことは困難です。試行錯誤しつつ行き当たりばったりで書くので、どうしてもぐちゃぐちゃしたプログラムになります。なので、プログラムをある程度綺麗に描こうと努力するのはもちろんですが、コメントを使ってプログラム全体の見通しを良くする方法も身につけておきましょう。

例えば以下のような読みにくい、長いプログラムがあるとしましょう:

何をしているのか、非常にわかりづらいプログラムです。実はこれは、入力された数値が素数かどうかを判定するプログラムです。括弧がたくさんあって読みにくいですね。しかし頑張って工夫しても、なかなかシンプルになりません。

そこでコメントの出番になります。例えば以下のようにコメントを入れてみましょう:

比較的読みやすくなりました。完璧とまではいきませんが、ある程度の流れは追いやすくなっているはずです。

この例では既に完成したプログラムにコメントを入れていきましたが、実際にはコメントを書きながらプログラムを書いて行く形になると思います。まず初めに大まかなコメントを入れて、それから書き始めるといいでしょう。例えば以下のような形からスタートすると良いと思います:

これは空っぽのプログラムですが、コメントがあることでだいたいの流れが作られています。あとは流れに沿って書いていくだけなので、非常に簡単になります。

コメントは、長いプログラム以外でも、もっと細かい規模で使うこともできます。例えば以下のようなプログラムの一部を考えましょう:

このとき、なぜ20文字なのでしょうか。5文字ではダメなのでしょうか。100文字にしてはいけないのでしょうか。少し不安になります。

しかしここにコメントがあったとします。例えば以下のようにです:

このコメントから、20という数字は考えて決められているとわかります。5ではおそらく少ないのでしょうし、100では大きすぎるのでしょう。

こういった風に、コメントでは、「この部分は何か(What)」よりも、「この部分はなぜこうなっているか(Why)」を書いた方が役立つと、世間一般では言われています。あまりこの説に縛られるのも毒ですが、ひとつの方針として心の中に持っておくと、あなたの助けになるでしょう。

例えば以下のようなプログラムを書いたとします:

これは配列をソート(並び替え)するプログラムです。アルゴリズムにはバブルソートというものを採用しています。そしてコメントには「なぜ(Why)」が書かれています。コメントから、このソート方法は遅いが、今回の場合は全く問題ないということがわかります。また、1万要素の配列をソートする場合はこのアルゴリズムは避けた方がいいということもわかります。プログラムを読んだだけではわからない情報が、コメントからたくさん読み取ることができます。

このようにして、プログラムにコメントをつけることで、追加の情報を付与して、読み手(ここでは自分自身)にとってわかりやすく仕上げることができます。

もっとコメントを入れる

先ほどもコメントについて言及しましたが、一般論についてでした。大学の実習授業においては、コメントにはもっと違った役割があります。

例えば一般的には以下のコメントは価値のないものとされています:

このコメントに価値がないとされるのは、これがプログラマにとって常識だからです。i++がiを1増やすのは当たり前のことで、このコメントはなんら新しい情報をもたらさないからです。

しかしプログラミング初心者にとっては違います。i++がiを1増やすことは当たり前ではありません。全く新しい概念です。ですから、このコメントにも価値があります。あなたの理解の助けになるのですから。

つまり、以下のようなコメントを書いてもいいということです:

このコメントにも価値があります。for文に初めて出会った場合、それが何をしているのかというのは一見してわかりません。そのときコメントがあれば、理解の助けになります。

授業内で使用するコメントは、あまり一般論に縛られる必要はありません。ノートがわりに様々なことを書いてもいいですし、思ったことをそのまま書いてもかまいません。とにかく何かしらの情報をプログラムに付け加えるということが重要になります。

役に立つ情報になるのなら、例えば以下のようなコメントでもかまいません:

このコメントからは、あなたが「よくわかっていない」ということがわかります。しかしこれは「正常に動く」ということもわかります。これもコメントとして有益です。何も書かないよりかは、はるかに多くの情報を得ることができるのですから。

コメントは「うまく動いていない」部分についても有効です。宿題課題のプログラムを書いていて、うまく解けずにふて寝して朝起きたら、普通の人間は、何があったか全く忘れてしまいます。そんなときにもコメントがあると、何がうまくいっていないのか、思い出すことができます。

こうしていれば、翌日の朝になっても、どこが動かなかったのか思い出すことができますし、スムーズに作業を再開できるはずです。

コメントを書くときに、あまり「かっこよくしよう」だとか、「ダサくないコメントを書こう」だとか、そういうことを気にする必要はありません。とにかく追加の情報となるものなら、何でも書いていいのです。何もコメントを書いていないよりかは、かっこ悪いコメントが書いてある方が、有益で、わかりやすいのですから。

ただし、実習授業内において、の話です。学校の外部に出すプログラムなどでは、こういうコメントは控えましょう。

printfデバッグをする

変数の名前などを工夫して、コメントも入れて、プログラムの流れをわかりやすくしても、それでもなおバグというものは多く発生するものです。これまでは遠回りしてきましたが、そろそろバグに直接立ち向かうための武器が必要です。

バグに直接対処する方法のひとつとして、printfデバッグと呼ばれるものがあります。これは名前の通り、printf関数を使って値を出力し、デバッグ(バグ取り)をする手法です。単純な方法ですが、実に効果的です。

実例を見てみましょう。以下のようなプログラムがあるとします:

こんなに簡単なプログラムなのに、バグが潜んでいます。これを実行して、値を入力しても、でたらめな値が出力されるだけです。

このとき、どこが悪いのかを調べてみましょう。やり方は簡単で、printf関数を使って、とりあえず値を出力してみるのです。今回は「そもそもinputを正確に受け取れているか?」という点が怪しそうです。一度input変数を表示してみましょう。

デバッグ用のprintf関数を追加しました。これで実行してみます。

inputの値がものすごいことになっています!つまり、これはinput変数を正確に受け取れていないということです。よって原因はscanf関数にある可能性が高いです。scanf関数について、教科書などで調べてみましょう。すると、double型で受け取る時には、「%d」ではなく「%lf」を使用するということがわかりました。さっそく修正してみましょう。

これで正確に動きました!あとは不要になったデバッグ用のprintf関数をコメントアウトしておきましょう。全く消してしまうよりかは、後のためにコメントとして残しておく方が無難です。

このようにして、printf関数を用いて途中の値を出力することで、バグの原因を特定し、修正することができました。

printfデバッグは簡単で強力な手法です。悩んだときには頼ってみましょう。課題で行き詰ったときに、プログラムとひたすらにらめっこしている学生もたびたび見受けられますが、そういうときにはとりあえず手を動かしてprintfデバッグしてみた方が解決できることが多いです。

もうひとつ例を見てみましょう。以下のプログログラムは、1^2+2^2+3^2+…+input_number^2を計算するものです:

しかしこれを実行しても「0」としか表示されません。何がいけないのでしょうか。printfデバッグで調べてみましょう。

デバッグ用にprintfを挿入しました。ループ計算の時は、「ループに使った変数i」「使用した計算」「結果を代入した変数」の3つを出力すれば、だいたい問題が絞り込めます。さらに色々な処理をしているなら、それも全部出力してみると原因を特定しやすくなるかもしれません。

これを実行してみましょう。

出力結果から、「i*iに問題はない」「totalに代入する時に0になっている」ことがわかります。よってtotalの代入式をよく見てみましょう……よく見ると「+=」とすべきところが「*=」となっています!これでは何度やっても0になるのは仕方ありません。

そしてさらにもうひとつのバグも見つかります。iは「1からinput_numberまで」のはずですが、出力を見ると「0からinput_number-1まで」となっており、ひとつずれています。これは変数の初期化とループ条件が間違っているということです。つまり、「i = 0」の部分を「i = 1」に、「i < input_number」の部分を「i <= input_number」に修正することで直すことができます。

これで修正できました!実行してみると、正しい出力が得られます。

このようにして、printfデバッグをうまく使えば、簡単にバグの特定ができます。自分が書いたプログラムがうまく動かないときは、画面を眺めてうんうん唸るよりも、とりあえずprintfデバッグに手を出して見ることをおすすめします。

[上級者向け]中間変数を使う

授業が先に進んで来ると、プログラムがどんどん複雑化していきます。複雑なプログラムは流れが追いにくく、バグが発生しても気づくことが難しくなります。

複雑なプログラムは、中間変数を使うことで、ある程度わかりやすくできます。わかりやすいプログラムを書くことができれば、バグの発見が簡単になります。

例えば以下のようなプログラムがあるとします:

このプログラムは点(1, 1)と点(2, 2)の2点間の距離を計算するもので、実行すると「1.41421」と表示されます。しかしなかなか大規模な計算式になっていて、タイプミスなどをしてしまいそうです。このような部分はバグの温床となります。

こういったときには中間変数を増やして、いくつかに分割してやると読みやすくなります。

動作は変わりませんが、だいぶ読みやすくなります。もっと細かく分けてもいいかもしれませんが、そこは個人の裁量次第です。

中間変数の使用は、if文においても有効です。例えば以下のようなプログラムがあるとします:

これは文字がアルファベットあるいは数であることを確かめるプログラムですが、if文の中がすごいことになっています。こういった巨大な条件式は、プログラムを書いているときは大丈夫なのですが、あとで読む時になると、なにをやっているのか少しわかりにくいです。

これも中間変数を用いることで読みやすくすることができます。

だいぶすっきりしました。変数の数は大幅に増えましたが、if文自体はシンプルになりました。

[上級者向け]関数を活用する

基礎課題をすべて提出して、応用課題などに手を出し始めると、プログラムが非常に複雑になってきます。複数の処理が入り乱れるからです。そういったときは関数を利用して処理を分割すれば、プログラムをシンプルに保つことができます。

しかし、関数の利用は、躊躇する学生が多いようです。それは「関数」という概念自体が難しいからでしょう。関数を用いて解決できる問題よりも、関数を用いることで発生する問題の方が多いとなると、なかなか手を出せません。それでも、関数というものは非常に便利なので、もし余裕があれば、ぜひ利用してみてください。

関数を使うと簡単になる例を紹介します。例えば以下のような複雑怪奇なプログラムがあるとします:

これは非常に読みにくいプログラムで、何をやっているのか、さっぱりわかりません。しかしこのままだとおそらく大量のバグに悩まされることになります。何とかしないといけません。

そこで関数を用いて処理を分割すれば、プログラムが綺麗になります。主に処理が複雑な部分を関数にすれば、だいたい綺麗になります。今回は素数判定部分を切り出せば簡単になりそうな気がします。実際にやってみましょう。

だいぶ綺麗になりました!まだまだ2重ループなどがあり複雑ではありますが、先ほどよりかは比較的読みやすいはずです。このように、関数で処理を切り出し分割することで、プログラムを綺麗に保つことができます。

関数は奥が深く、様々な場面で利用することができます。関数については、記事「JavaScriptの関数で何ができるのか、もう一度考える」でも詳しく解説しているので、そちらも読んでみてください。

まとめ

  • 実習において気をつけたいこと
    • 積極的に質問する
      • 自分で考えることも大事ですが、せっかくなのですから先生やTAに質問してみましょう。
    • 本やネットで調べる
      • 複数の本を参照したり、ネットの質問サイトを活用してみましょう。
  • プログラミングにおいて気をつけたいこと
    • エラーメッセージを読む
      • エラーメッセージには多くの情報が含まれています。あなたの助けになるかもしれません。
    • プログラムを読みやすくする
      • 読みやすいプログラムはバグを減少させます。できるだけ読みやすく書いてみましょう。
    • わかりやすい変数名をつける
      • 変数名がわかりやすければ、ミスも見つけやすくなります。
    • コメントを入れる
      • コメントがあれば、プログラムを読むだけではわからない情報を手に入れることができます。
    • もっとコメントを入れる
      • あまりかっこよくはないコメントでも実習授業では大いに役立ちます。なんでもコメントに書いてみましょう。
    • printfデバッグをする
      • printfで途中の値を出力すれば、バグの原因が何か見えてくるかもしれません。
    • [上級者向け]中間変数を使う
      • 中間変数を使えば、複雑な式を複数に分割することができます。
    • [上級者向け]関数を活用する
      • 関数を使えば、長いプログラムを細かく分けることができます。