2019年11月11日追記
ただのタイトルで煽ってるだけの記事に半年経っても未だに大量のアクセスがあるので追記しておきます。
ここで言いたいことは、「プログラマならコンピュータサイエンスを勉強してると役に立つよね」、ということ だけ です。
この一文以上に有用な言葉は以降の文章では出てきません。みなさんの時間を無駄にしないために注意書きをしました。
それでも良いという人は読んでみてください。
Twitterで「〇〇ができるという人が面接に来たけど、『じゃあXXXやYYYって知ってます?』というと知らないという人が多いんだよねぇ」とかいうツイートを見かけて、私はXXXやYYYってのを知らなかったので調べた見たところ、常識とまでは言えない概念だったり、名前は知らなくても誰もが知ってる概念だったり、むしろもっと良いアプローチがあるのではという思想だったりでなんだかなぁと思っていたところ、半日くらいで多方から総ツッコミが入っていたので楽しかった。
楽しかったので、私も「プログラマだったら当然知ってるよなぁ?」という上から目線な記事を書いて炎上させたいと思って書いてみた。いや、真面目に書いてますよ。
ネット上でのIT系エンジニアはどうも最新の技術を追い続けていくことに偏重しがちで、基礎について語ってる人って以外に居ないよねー(知ってるから当然だから言う必要もないのかな)などと日頃おもってたので、そういう基礎にフォーカスしてます。
この業界で「Aという新しい技術がすごい!」とかよく言われて、よくよく調べるとAという技術は昔から使われていたBという技術の焼き直しだったり言い方を変えただけだったりするのはとてもよくあることなので、そういうのに振り回されずに本当に大切なことを列挙してみます。
プログラマだったら当然知っててほしいと思う知識
データ構造
リスト、配列、ツリー、ハッシュ、スタック、キューなどの違いが説明でき、自分がこれから組もうとしているプログラムについてどれを使うべきか適切に選択できる。
必要に応じてそれらを応用したデータ構造を自分で定義して使うことができる。
計算量(計算複雑性)
自分が書いたコードが O(1)
なのか O(n)
なのか O(n^2)
なのか O(n^m)
なのか O(n!)
なのか … を説明できる。
各種あるプログラミング言語のコレクション関係のライブラリで、どの操作がどの計算量で終わるのかを理解した上で使うことができる。
計算複雑性クラスの概要を知っていて、それぞれに例えばどういう問題(巡回セールスマンとかナップザックとか集合打とか)があるのか、少なくとも一度は勉強したことがある。
探索や計算にめちゃめちゃ時間がかかる仕様が盛り込まれたときにそれを適切に説明し、場合によって然るべきインデックス等々を実装できる。
アルゴリズム
ソート、探索、文字列検索、データ符号化、圧縮、誤り検出・訂正など。「なんでソートが遅いのか」「なんで圧縮してもサイズが減らないのか(圧縮が無意味なのか)」などを適宜判断して修正できる。
数値計算
浮動小数点の仕組みと情報落ち桁落ち等の理解、行列、線形代数、モンテカルロ法、ニュートン法、オイラー法、ルンゲクッタなど。「そういうのがあったな」と頭の片隅においといて必要なときにググりつつ応用できる。
グラフ理論
有向・無向グラフ、木、ループ、カット、彩色、結婚定理とかの基礎の理解。「そういうのがあったな」と頭の片隅においといて必要なときにググりつつ応用できる。
理論計算機
ステートマシン、オートマトン、チューリングマシン、正規表現、ラムダ計算など。「そういうのがあったな」と頭の片隅においといて必要なときにググりつつその考え方等々を応用できる。
セキュリティ・暗号化
公開鍵認証、共通鍵、ハッシュ(ダイジェスト)、署名、乱数生成のためによく使われるアルゴリズムとその使い方。アルゴリズムそのものに対する簡単な理解(さらでアルゴリズムを書ける必要はないが、だいたいどういう原理で何をやっているのかを説明できる程度の知識)。
プログラミングパラダイム
手続き、構造型、オブジェクト指向、関数型、静的型付け、動的型付け、ダックタイピングなどがそれぞれどんな特徴を持っているか理解していることと、好みはあれど特定の一つが最強と思い込まず柔軟に使い分けることができる寛容さ。
並列・非同期処理の基礎
並列処理と並行処理の違い、並列処理が困難であるという認識、並列処理プログラムのデバッグができること、スレッド、ブロッキング、メッセージング、セマフォ、ミューテックスあたりの理解。
Future, Promise, async, awaitを使え、かつ裏で何をやっているのか説明でき、どんなときに何を使うべきか、何を使ってはならないのかを判断できる。
できれば知っておいたほうが良いと思う知識
ソフトウェア工学
設計論、エンジニアリングなど。ウォーターフォール、アジャイル、DDDとかそういうのとその実践経験。
現代コンピュータの性能
どのメーカーでどんなCPUがあって、だいたいどれくらいの性能で、シングルスレッド性能とコア数の違いを理解しているとか、各種ストレージやメモリへのおおまかなアクセス速度の差などを理解してどんなコードを書いたらどのくらい時間がかかるかという見積もりができる。
CPUの仕組み
どういう命令セットがあって、CPUにはどんなモジュールが乗っていて、分岐予測やハイパースレッディングは何をやっているのかとか理解できる。
OSの仕組み
メモリ空間、メモリ管理、仮想記憶、シグナル、IO、割り込み処理、タスク管理、ファイルシステムなど。
ネットワーク(7階層モデル, IP, TCP, UDP, HTTP, HTTPSなど)
なんかよくわかんないけどネットワークがつながらない、うまくうごかない、みたいなときにWiresharkやtcpdump, ngrepなどでメッセージをダンプして原因を判断できる。OSがネットワーク関連のリソースをどう管理しているか大まかなところを理解している。TCPの状態遷移を大まかに理解していて、netstatとかnmapとか基本的なコマンドを使用して知りたい情報を取得できる。
セキュリティ
WifiやHTTPSでの鍵交換で何をやってるのかとか、どういうコードを書いたらどういう脆弱性が生まれるとか、世間での攻撃手法として一般的なものは何で、その対策としてどういうことをしておく必要があるのかなどをある程度語れる。重要情報を平文で保存しない。方法の秘匿で担保するような方法(方法やソースがバレたらクラッキングも容易にできる方法)やバックドアを無闇矢鱈につかわない。
統計・データマイニング・機械学習系の基礎知識
回帰分析、最小二乗法、ニューラルネット、SVMとか?個人的には必要になったときにやり始めればいいと思うものの流行りに乗っかって一応書いてみた。
まとめ
一言で言うとここに書いてることは「コンピュータサイエンスを勉強すべき」ということなのかもしれません。
適当に書いたので「おいおいそれ入れるならこっちも入れるべきだろ」 みたいなのはあると思いますが許してください。ただ、全体の方向性としては大体のプログラマに同意してもらえるのでは、と思ってます。
上記に挙げられてることを知らなかったとしても、「マニアックな知識を要求してマウントを取ってる!」とかひねくれたことを思わず、勉強してみましょう。きっと将来役に立ちますよ。
追記→プログラマだったら当然知ってるよね?という知識一覧の補足