本記事は『「技術書」の読書術 達人が教える選び方・読み方・情報発信&共有のコツとテクニック』の「2-1 比べて、使い分ける。時間をムダにせず理解を深める(著:増井敏克)」から「プログラミング書の読み方〜動くコードを自分で書く〜」を抜粋したものです。掲載にあたって編集しています。
プログラミングを学ぶゴールを意識する
プログラミングは目的ではなく手段
プログラミングを学ぶときに挫折する人は多いでしょう。その理由として、「プログラミングを学ぶこと」が目的になっている可能性が挙げられます。
プログラミングを学ぼうとすると、最初はつまらない例がたくさん登場します。「Hello World」と出力できる、といわれても、それならプログラミングをするまでもないと感じるかもしれません。単純な四則演算について学んでも、それなら電卓で十分だと思う人もいるでしょう。配列やリストといったデータ構造を学んでも、普段使っているソフトウェアとの間に大きなギャップがあるのです。
これは数学を学ぶときでも同じことがいえます。子どもたちが数学を学ぶときに、「こんなこと知らなくても生きていける」と言い訳をするのと同じで、基礎的な技術が必要なことは理解していても、それが現実とかけ離れていると学ぶ意欲が湧かないのです。
プログラミングは目的ではなく手段です。もし便利なツールやアプリが存在しているのであれば、自分でプログラムを作る必要はなく、それを使えばいいのです。世の中に存在しないから自分で作る必要があり、そしてそれを実現するために基礎的な技術を学ぶという順番になります。
作りたいものをイメージして、まずはざっと目を通す
つまり、プログラミングについての本を読むときには、「最終的に自分が作りたいもの」を意識して読むことをおすすめします。もちろん、本を読んでいると、自分が作りたいものに関係のないことがたくさん出てきます。本の内容を前から順にすべてを納得できる、実装できるようになるまで理解している時間もないことが多いでしょう。そこで、まずはざっと読み通すことをおすすめします。
最初はとりあえず1冊の本に書かれている内容を把握するために読みます。使わないものを詳しく知っても意味がないため、「そんな機能があるんだな」というくらいで読み飛ばすのです。
そして、基本的な概念や使い方を確認します。その本で使われているソフトウェアであればインストールしてみて、数行のソースコードであれば入力して試してみてもいいでしょう。
このとき、うまく動かない、となったり、すぐに解決できないと感じたりするのであれば、とりあえず飛ばします。大切なのは「何がどこに書かれているか」を把握することです。
そして、読みながら「何かに使えないか考える」という思考を繰り返します。自分が作りたいものに関係なくても、「こんな技術があるなら、こんな機能を追加できるかも」と思いながら読むと、発想が広がります。
複数のプログラミング言語を比べながら読む
複数言語の習得はスキルアップの常道
プログラミングを学ぶとき、どのプログラミング言語を学べばよいのか迷うことはよくあります。しかし、まずは1つのプログラミング言語を学ぶことが大切です。それだけでなく、1つの言語で「動くものを作れるようになる」ことはもっと大事です。しかも、書籍のサンプルを少し変えたレベルではなく、実際に仕事などに役立つレベルのものを作れるようになることを最初は目指しましょう。
この知識と技術が身につけば、次に目指すのは、複数のプログラミング言語を使えるようになることです。書籍『達人プログラマー』では「毎年少なくとも一つの言語を学習する」ことが推奨されています。実際、毎年でなくとも、2つ、3つとプログラミング言語を学んでいくと、1つのプログラミング言語では得られなかった知識が得られます。
1つを学ぶだけでも大変なのに、複数のプログラミング言語を学ぶのは困難だと感じるかもしれません。しかし、1つのプログラミング言語を知っておけば、ほかの言語を学ぶのはそれほど大変ではありません。
変数や関数といったプログラミングの基本や、アルゴリズムなどの考え方はどの言語でも通用します。微妙な書き方の違いを把握するだけで、ほかの言語でもプログラミングができるようになります。
複数言語を学ぶときのコツ
最初は似た種類の言語を学ぶことをおすすめします。Rubyを学んだ後にPythonを学ぶ、Javaを学んだ後にC#を学ぶなど、近い種類の言語を選べば、プログラミングについての基本的な考え方をあらためて理解でき、そのうえで言語間の違いを比較できます。
3つ目以降の言語は、可能であれば種類の違う言語を学ぶことをおすすめします。最初に学んだものがC言語のようなコンパイル型の言語であれば、次はRubyやPHPのようなインタプリタ型の言語を試す、Javaのようなオブジェクト指向型の言語を学んだら、Haskellのような関数型の言語を学ぶ、といった形です。これにより、プログラムの実装方法の違いや考え方の違いを理解できます。
そして、可能であれば複数のプログラミング言語の本を同時並行で読み進めて、同じ処理を実現するプログラムを作ってみましょう。複数の言語を学ぶと、書き方の違いで混乱する人もいます。しかし、逆に違いを理解することで、ほかの書き方ができないかを考えられます。
複数の言語の書き方を比べた本を読むのも1つの方法です。たとえば、私の本だと『もっとプログラマ脳を鍛える数学パズル』ではRubyとJavaScriptのソースコードを見比べながら読めます。また、『RとPythonで学ぶ統計学入門』ではRとPythonのソースコードを比べられます。『プログラミング言語図鑑』のように、多種多様な言語をざっくり知るのもよいでしょう。
ここで大切なのは、それぞれの言語で実務レベルのプログラムを実装できるようになることではありません。普段から実務としてプログラムを書くのは2〜3個程度でしょう。それよりも、ほかの言語での書き方を知ることで、自分が普段使っている言語が持つ特徴をより深く理解し、工夫できる可能性があるのです。
長いソースコードを読むとき
プログラミングの本を読んでいると、1ページすべてがソースコードであることは珍しくありません。初心者のうちは、このソースコードを読むことも大変です。本のソースコードを見て、すぐに挫折してしまう人もいるでしょう。
しかし、長いソースコードでも、それぞれは小さなソースコードが少しずつ増えてできたものです。そこで、こういったソースコードを読むときには、「塊」を意識することがポイントです。たとえば、1つの関数の中でも、ループ(繰り返し)は1つの塊だといえます。ifによる条件分岐も、それで1つの塊です。一般的なプログラミング言語では、インデントが揃っているところが1つの塊だと考えます。
そして、その塊の単位で処理を理解していきます。もう少し細かく見たい場合は、どれが変数でどれが関数なのかをマーカーで色をつけて判別してもいいでしょう。最近はカラー印刷の本も増えていますし、テキストエディタでも変数や関数には色がついていますが、プログラミング関連の本は白黒のものが多いです。白黒のソースコードを読むときも、色をつけることで少しわかりやすくなります。
フローチャートを描くのも1つの方法です。最近はフローチャートを描くことは少なくなったように感じていますが、複雑な処理の流れを理解するときにフローチャートは有効です。読むだけでなく、ソースコードを書くときにも、複雑な処理になりそうな場合はフローチャートを描いて、塊ごとに整理するだけで、ソースコードがシンプルに書ける、抜けや漏れがなくなる、といった効果があります。
実務で使うプログラムでは、数百行、数千行というソースコードを読まなければなりません。つまり、書籍の数十行のソースコードが読めなければ、仕事でプログラミングをすることはできません。
動くものを作る
よいコードより動くコード
次から次へと本を読んでいても、読むだけで自分ではプログラムを作ろうとしない人がいます。状況を聞いてみると、「要件定義と設計を丁寧にしないと後で修正が大変だと本に書かれていた」という理由が返ってくることがあります。
これは事実で、企業でのシステム開発であれば、手戻りを防ぐために要件定義や設計という作業をして、ドキュメントを作成し、仕様が確定してから開発することはよくあります。
しかし、プログラミングを学び始めている段階では、細かく仕様を決める必要はありません。もちろん、決められればそれに越したことはないのですが、まだプログラミングについて深く理解できていない状態で、仕様を固めようとしても難しいものです。
結果として、想定していた仕様を実装してみると、そもそも論理的に破綻している、設計が悪くて、結局後からの修正が大変になることは珍しくありません。仕様に問題がないかを判断するためには、それなりにソースコードを書き、何度も試行錯誤を繰り返す必要があります。
このため、最初からきれいな「よいコード」を書くことを考えるのではなく、まずは汚くても「動くコード」を書くことを心がけます。たとえば、その本の全体を一度読み終えた段階で、章単位で学んだことを使って、何か動くものを作ります。本に書かれている内容をコピーするのではなく、ゼロから何か新しいテーマを考えて作ってみるのです。
もちろん、本に書かれている内容をそのまま入力して、一部を書き換えて動かしてみる、章末に書かれている練習問題を解く、というのもいいでしょう。その本に書かれている通りに実現できなくても、インターネットなどを調べれば答えが見つかるかもしれません。このときは1冊の本にこだわる必要はありません。
本は体系的にまとめられていますが、それを順に読まないといけないわけではありません。ほかの本や雑誌、インターネット、知人などあらゆるものを組み合わせて、とりあえず動くものを作ることの方が重要です。そして、動くものができてから、もう一度設計を考え直してみればよいのです。
エラーの発生を恐れない
プログラムを作っていると、うまく動かないことも出てきます。本に書かれているソースコードを同じように入力しているのに、エラーが出てうまく動かない、という状況が発生します。多くの場合、入力ミスや操作ミス、なんらかの設定ミスなど読者側の原因がほとんどですが、場合によってはソフトウェアの不具合や、執筆時点から時間が経ってソフトウェアの仕様が変わっている可能性もあります。著者による検証不足などで、そもそも書いてあることが間違っているケースもあります。
このとき、エラーメッセージを確認し、もしメッセージの内容を理解できれば、その内容をもとに原因を調査します。多くの本では、エラーが起きたときにどのようなメッセージが表示されるのか、具体的な内容は書かれていません。ソースコードをそのまま入力すればうまく動くことを前提として書かれていますし、入力ミスや設定ミスをすべて網羅して本を書くことは不可能です。
このため、エラーが発生したとき、多くのエラーについては自力で解決する必要があります。エラーメッセージが表示されたとき、その内容をそのままインターネットで検索する方法も有効です。同じようなエラーが表示された人が、その解決策をブログなどに書き込んでいるかもしれません。
もちろん、エラーメッセージにはなんらかのヒントが書かれていたり、原因が表示されていたりするので、それを読み解くことは大切です(多くの場合、エラーメッセージは英語ですが、それほど長い文章でもないので、単語を調べれば意味が理解できることは多いものです)。
エラーが出ると、「やはりプログラミングは難しい」と感じる人がいるかもしれませんが、スムーズにいくと学べないことはたくさんあります。エラーが出るということは、その対処も学べるということです。
実務においては、エラーが出ないことはほとんどありません。エラーに対応することを繰り返すことで、エラーへの抵抗感がなくなり、スキルアップにもつながるのです。
同じ結果が得られる複数の実装を試す
プログラムを実装するとき、その書き方は1通りではありません。同じ結果が得られて、処理にかかる時間が同じでも、その実装方法はたくさんあります。上記のように、とりあえず実装してみないと、その良し悪しがわからないこともあります。アルゴリズムの本を読むと、高速なアルゴリズムに書き換える内容が掲載されていますが、複数の実装を試すのは処理速度を追求するためにアルゴリズムを変えるときだけではありません。
このため、ソースコードの読みやすさや保守性などを考えて、様々な実装方法を試すことは有効です。アルゴリズムに関する本でなければ、書籍には1通りの実装しか掲載されていないことがほとんどですが、同じ処理を複数の書き方で実装することで、読みやすさが大きく変わります。このとき、ほかの書き方を知らないと、書き換えようという気になりません。
プログラミングの世界では、「リファクタリング」という言葉がよく使われます。リファクタリングでは、結果を変えずに実装を変えます。同じ処理結果が得られるけれど、保守しやすい、再利用しやすい、処理速度が速い、メモリ使用量が少ないなど、様々な理由で「よりよい」ソースコードに書き換えることを指します。
複数の実装のうち、どれがよいかを知らないと、リファクタリングはできません。つまり、リファクタリングする前に、ほかにどんな実装方法があるのかを知る必要があるのです。このとき、本のソースコードを違う方法で実装することは有効です。本には1通りしか実装方法が載っていませんが、これを自分なりの方法で書き換えてみるのです。そして、本のソースコードと、自分が書き換えたソースコードを見比べて、どちらが読みやすいか、どちらが保守しやすいかを考えるのです。
場合によっては、ゼロからもう一度作ってみることも有効です。時間の無駄のように感じるかもしれませんが、同じ機能を実現するプログラムを、より保守しやすいように設計できるようになるためには必要なステップです。
自分の手で確かめる
他人のコードを信じない
世の中にはGitHubなどにあるオープンソースのコードや、ブログ上に誰かが書いたコードなど多くのコードが公開されています。これらの一部をコピーしてプログラムを作る、もしくは参考にして書き換えることもあるでしょう(もちろん、ライセンスには注意したうえで)。また、フレームワークやライブラリ、Web APIなど、他社が開発した機能を呼び出して使うこともあります。これらは中身がどのように実装されているかを確認することなく、便利な機能として使うこともあります。
しかし、これらのソースコードや製品に不具合がないわけではありません。便利な機能であっても、そこに不具合や脆弱性が残っている可能性があります。このとき、信じられるのは自分が書いて、自分がテストしたものだけです。
これは書籍に載っているコードでも同じです。書籍では、処理内容の解説とあわせてソースコードが掲載されています。そして、見た目ではそのように動くように見えます。しかし見た目では正しそうに見えても、実際に動かしてみると違う結果が返ってくることがあるのです。著者のPCで得られた結果が、読者が手元のPCで試すと違う結果になるかもしれません。このとき、信じられるのは手元にあるPCだけです。著者の勘違いなのかもしれませんし、著者のPCではなんらかの設定によって動作が違うのかもしれません。
この考え方は実務でも有効です。ドキュメントに書かれている内容とソースコードが違うことは少なくありません。開発した当初は一致していても、時間が経つにつれて何度も修正が発生し、誰かがドキュメントの修正を忘れた、という状況が発生します。
このときも、ドキュメントを信じるのではなくソースコードを見ますが、既存のライブラリなどを呼び出しているときも、本当にそのライブラリが仕様通りの動きをしているかはわからないものです。ライブラリのバージョンアップによって、処理内容や結果が変わっている可能性があるのです。
そこで、本を読むときも、必ず自分の手で実行して、結果を確認するとよいでしょう。
処理途中の値を確認する
プログラミングの本では、ソースコードだけでなく、その実行結果も書かれています。ただし、このときに書かれているのは、その処理が終わった後の状態だけです。
たとえば、次のようなループ処理の例があったとします。このとき、本では実行結果だけが書かれています。このときに大切なのは、処理の途中で値がどのように変わっているのかを知ることです。
int sum = 0; for (int i = 0; i < 10; i++) { sum += i; } printf("%d", sum);
実行結果:45
このような例であれば、自分で実行することも大切ですが、次のような1行を追加して実行します。これにより、処理の途中で値がどのように変わるのかを確認できます。
int sum = 0; for (int i = 0; i < 10; i++) { sum += i; printf("%d\n", sum); } printf("%d", sum);
上記のような単純な処理なら、書かなくてもわかるという人は多いでしょう。しかし、処理が複雑になると、ただ本を読んでいるだけではなく、自分でソースコードに少し手を加えながら読むことが大切になります。
もちろん、上記のようにソースコードを変更するのではなく、IDE(統合開発環境)が備えるデバッグ機能を使う方法もあります。どのような方法を使っても構いませんが、処理途中の値を確認する癖をつけることで、プログラムの内部での動作を詳しく理解することにつながります。