プログラミングを学ぶ上で大事なこと
はじめに
某企業の新人エンジニア数名のJava研修講師を1ヶ月ほど担当しました。内容は、Javaの入門から、データベース概論、Spring Bootを利用したWebアプリケーション開発まで結構盛りだくさん。全員、ほぼJavaプログラミングは初心者で、最初は大丈夫かと思ってましたが、なんとか最後まで無事(?)乗り切れました。
さて、全員スタート地点はほぼ同じでも、最終的な到達点が全然違うのが面白いところです。文法の基本的なところも理解できないままの人、一度できた課題ならできるけど、少し問題を変えると途端に出来なくなる人、Webアプリケーションの開発スキル(といってもまだまだ基本ですが!)まで一気にクリア出来た人・・・
このような違いが生まれる原因は、本人の才能や適正ももちろんあるとは思いますが、パターン(本質)をきちんと理解できていないことが大きいと思います。今回は、主に新人プログラマ向けに、このパターンの理解不足について考察してみたいと思います。
パターンの理解不足
パターン という言葉には、様々な意味がありますが、ここでは型という意味で用います。ソフトウェアの分野ならば、デザインパターンが有名ですが、これもこの型という意味を指しています。デザインパターンとは、過去に開発された膨大なソフトウェアを分析し、その中でよく現れる設計方法や暗黙知をそれぞれ名前をつけてカタログ化したものです。
同じようなシステムでも、要求、開発エンジニア、利用するプログラミング言語、等々様々な要因が複雑に絡まって、全く異なるソフトウェアが出来上がるものですが、その設計方法というのはそれほど変わらず、デザインパターンを使うことにより、表層的な違いは無視して本質的な部分で会話したり思考したりすることができるようになります。
プログラミングを学習する上でも、この型という意味でのパターンを理解することが重要になってきます。ここでは、文法上のパターンとアルゴリズム上の2つのパターンに分けて考えてみましょう。
文法パターンの理解不足
Javaで変数を宣言して初期化する場合は以下のように書きます。
int age = 29;
String name = "menta";
boolean isMentar = true;
一見、様々な書き方があるように思えますが、以下のようにパターン化することが出来ます。
型名 変数名 = 値;
当たり前だろとツッコミを入れられそうですが、プログラミングを中々理解できない人は、整数型ならばこう書く、文字列型ならばこう・・・という感じで、ものすごく細かい部分に囚われて中々応用が効いていない気がします。
もう一つの例であれば、Javaでメソッド(関数)の宣言は以下のように書きます。
public int sum(int x, int y);
これも以下のようにパターン化出来ますが、おそらくパターンをよく理解していないため、思わずため息を付きたくなるような関数宣言をする人がいます。
アクセス指定子 戻り値の型 関数名(変数宣言のリスト)
その他も、if
文、for
文など、書く構文ごとに様々なパターンがありますが、プログラミングの上達を妨げている1つの要因がこのような文法パターンをしっかりと理解できていないところにあるのではないでしょうか。
アルゴリズムパターンの理解不足
アルゴリズム とは問題を解くための手順を抽象的に表現したもので、特定の言語には依存しない汎用化されたものです。初心者プログラマはアルゴリズムをしっかり理解せずに、言語固有の文法に囚われて1ステップずつ問題を考えていることが多いように思えます。
例えば、配列やリストというのはどのような言語でも基本となるデータ型ですが、配列に対する処理は大きく分けて、以下の3つに大別されます。
処理 | 概要 | 例 |
---|---|---|
フィルター | 配列の中からある条件を満たす要素を抽出して新しい配列を生成すること | 整数の配列から偶数だけの要素を取り出す |
マップ | 配列の全ての要素を別の要素に変換した新しい配列を生成すること | 整数の配列の全ての要素を2倍にする |
簡約 | 配列の全ての要素に対して何らかの操作を行い1つの値を生成すること | 整数の配列の全ての要素の合計を求める |
配列に対する大抵の問題はどんな複雑なものであっても、本質的にはこの3つの組み合わせで解決することが出来ます。経験豊富なエンジニアなら、一瞬で頭のなかに大まかなイメージが浮かぶものです。ただ、初心者プログラマや配列処理を苦手とする人は、これらのパターンをしっかりと理解できていないため、どうしても、小手先というか手続的なイメージでステップ単位で考えようとする。
具体例を挙げましょう。例えば、お店で販売するお弁当のデータがあるとします。お弁当のデータ一覧から和食のお弁当だけのカロリー平均を求めたいとしましょう。経験豊富なエンジニアであれば、一瞬で以下をイメージします。
- お弁当データから和食の弁当のみをフィルター抽出
- 1の結果をマップしてカロリー一覧を生成
- 2の結果を簡約して合計を求めて平均値を算出
これをJavaで書くと以下の感じになります。
public double average(List<Bento> bentoList) {
return bentoList.stream()
.filter(bento -> bento.kind == Kind.Wasyoku)
.mapToDouble(Bento::calory)
.average().orElse(0);
}
一方、プログラミング初学者や勉強不足の人は、お弁当を一個ずつループして和食だけを取り出して・・・のような超具体的なイメージで考えているようです。
パターンを理解するにはどうしたらいいか?
反復練習する
パターンはプログラミングに固有の概念ではなく、どのような分野でもパターンを理解することが重要だと思います。例えば、空手のカタや野球のフォームなどはまさに型(パターン)です。フォームが確立されて初めて、どんな変化球にも対応できるようになる。優秀な野球の選手は毎日素振りを続けることで自身のフォームを追求しますよね。
これと同じで、結局パターンを身につけるにはプログラムを書き続けるのが近道だと思います。ただ、そこで重要なのは、きちんと問題意識を持つことです。ただ素振りを続けるのは筋トレしてるだけと同じで、ただプログラムを書くのはタイピングの練習に過ぎません。
複数の言語を勉強する
プログラミング言語は様々なパラダイムに分類することができます。有名なパラダイムとしては、関数型言語、オブジェクト指向型言語、命令型言語などが知られています。パラダイムが異なれば、同じ問題でもプログラミングの考え方そのものが全く違った見え方になります。
1つのプログラミング言語だけに固執すると、どうしてもその言語の考え方に引っ張られてしまいます。複数の言語を勉強することで、同じ問題を様々な角度で眺めることができるようになり、アルゴリズムの本質が見えてくるようになります。
特に、最近は様々な言語に関数型言語のエッセンスが取り入れられつつあります。関数型言語は宣言型言語と呼ばれたりもしますが、関数型言語を学ぶことで、問題をより抽象的に捉える能力を養うことができます。
様々な文献を読む
デザインパターンはGoF (Gang of Four)と呼ばれる4人の凄腕プログラマやエンジニアが1994年に発表したものです。このように、世界中には日本を含めて優秀なプログラマがたくさんいます。そしてこのような優秀なエンジニアは書籍やブログを通して、ソフトウェア開発に関する様々な方法論を発信し続けています。
まずはいろんな文献を読むことで、ソフトウェアに関する様々なパターンを知ることから初める必要があります。そもそもどういうパターンがあるのかをを知らないと始まらないですよね?もちろん、自分でプログラミングを続けることで、その真理に到達することはあるでしょう。でも、それにはものすごい年月がかかるでしょう。それよりも、優れた先人が残してくれた遺産(パターン)を勉強するのが近道です。
メンターを見つける
とはいえ、たった一人で勉強を続けるのは中々困難な道のりです。正しい道を進んでいるのかも分からないですよね。野球にコーチが必要なように、プログラマにもコーチ(メンター)が必要です。身近に優秀なメンターを見つけましょう。そして、積極的に相談し、良いところを盗んでください。
まとめ
プログラミング初心者にとって大事なことはパターン(本質)を理解することです。今回は文法上のパターンとアルゴリズム上のパターンを例にとってその理由について説明しました。
パターンを身につけるには、まずは実際にプログラムを書き続けることです。ただ書くだけではなく、常に客観視して改善できるところを探しましょう。
また、世の中にどのようなパターンがあるのかを知らないと話になりません。優秀な先人が発見したパターン自体を書籍やブログなどを通して勉強しましょう。
複数の言語を勉強することで問題を様々な視点で見ることができるようになります。特に、関数型言語は様々な言語に取り入れられつつあります。抽象的な視点を養う上でも関数型言語を勉強してみてください。
そして、やはり一人で勉強するには限界があるでしょう。いろんな人の意見に耳を傾けてみましょう。メンターを見つけるがもっとも手っ取り早いです。私もお手伝いしますよ。お気軽にお声をおかけください。