Something like blog
< JavaScriptにまつわるエトセトラ | 新型コロナウイルスとPCR >

プログラマーにアルゴリズム脳はいらない

プログラマーの適正としてアルゴリズム脳が挙げられます。

しかし、最近この能力はそれほど大事ではないし、むしろ仕事においてはデメリットのほうが大きいのでは?と考えるようになりました。

そこで今回はアルゴリズム周りの能力について考えてみます。

ちなみに、アルゴリズム構築能力は大事だと思っています。

当エントリではアルゴリズム脳は既存の知識や脳内思考のことを指し、アルゴリズム構築能力は結果的にプログラムを書き上げる力、と分別して考えます。

仕事におけるプログラミングでは最初に頭の中で考えたものがそのままサービスで動くコードに置き換わることはあまりありません。

この「最初に頭の中で考える」がいわゆるアルゴリズムを考えることです。

「最初に」と書きましたが、最初に書き下したアルゴリズム実装(ある処理の一連のコード)がそのまま本番環境のデプロイまで残っていることはあまりないと思います。

書き終わったあとに、実際に動かそうとしてみたところで、コンパイルエラーだったり、インターナルサーバーエラーだったり、テストがコケたり、想定していた動きと違う…など、何かしらの不具合が見つかるのが普通です。

逆に、長めのコードを書いたあとに一回でコンパイルが通ってしまうと逆に「えっ!?うそ?」という気分になります。

最初に書いたコードからいろいろトライアンドエラーを重ねて不具合を解消していき、第三者のレビューやCIのチェックを超えて、デプロイされ、やっと「動くコード」として完成するのです。

この流れで見るとアルゴリズムを考える作業は最初だけで、仕事全体で見ればほんの一部だということが分かります。

しかもプログラミングというかシステム開発で一番大事なのは、個々の処理のアルゴリズムではなく、実装する前のモデリングや設計です。

部分的な実装が致命的な障害になる場合もありますが、設計やモデリングの失敗に比べれば傷はまだ浅いほうです。

システム開発における個人のアルゴリズム能力の重要度はそれほど高くないといえます。

そして、アルゴリズムを考えるのが苦手な人でも、プログラミングは全然可能であると考えます。

自分はどちらかというとアルゴリズムを考えるのが苦手な人です。

ここで自分がコードを書くときの例をあげます。

実装したい仕様があったとき、最初に「なんとなくこんな感じかな」とあまりロジカルというかMECE的にやろうとは考えずに、とりあえずそれっぽいコードを一旦書いてみます。

書いてみて実際に動かしてみた結果と仕様を比較しながら「ここはこうじゃなくてこうかな?」というふうに試行錯誤しながら直していって最終的に仕様と同じ挙動になったところで一旦完成としています。

具体的な実装例で説明してみましょう。

「今日が作成日から三日以内なら真」という仕様があるとします。

実装しようとする場合、まずは「作成日と3日で比較すればいいんだな」という安易な発想で

created_at < Time.current + 3.days

みたいに書いてみます。(言語はRuby with ActiveSupport)

で、実際に動かして検証してみます。

すると「あ、現在より過去のデータだとダメだわ」と気づきます。

次にそれを修正して

Time.current < created_at && created_at < Time.current + 3.days

としました。

これでどうでしょうか?

過去のデータはちゃんとはじいているようにみえます。

次に「3日以内ということは今日を含んで、明日と明後日までが対象だな」ということでそれぞれの日付を入れて実際にテストしたところ、今度は今日がダメだと分かります。

更に修正して

Time.current <= created_at  && created_at < Time.current + 3.days

としました。

これでもまだダメなようです。

今日だとしても実行時間によって結果が分かれてしまいます。

Time.currentは時分秒も含まれるのでcreated_atが時分秒を含んだデータの場合(Railsの命名規約的には含んでいます)、作成日が現在時刻より過去なら同日でも偽判定となってしまいます。

ということでさらに

Date.today <= created_at  && created_at < Date.today + 3.days

としました。

これでうまく仕様を満たしたっぽいです。

さらに「Time型とDate型が混同しててなんか分かりにくかったからダメだったんだな」と思い至り、最終的に

(Date.today..(Date.today + 2.days)).include?(created_at.to_date)

としました。

最初のコードは確かに間違えていて「お前はバカか?」と言いたくなる感じですが、最終的には仕様どおりに動くプログラムができあがりました。

はじめから最後にできあがったようなコードが書ければいいですが、それができなくてもトライアンドエラーを重ねることによって仕様を満たすコードを実装することができます。

さらにトライアンドエラーを重ねて実装していくほうが一発で仕上げるよりもメリットがあります。

仕様の抜け漏れに気づきやすいし、実際の実行結果をもとにコードを洗練させていくので、頭の中だけで考えるより現実的なロジックに到達しやすいのです。

何だったらこの試行錯誤こそがTDDの本質と言えます。

逆に、アルゴリズムを考える能力が高かったらどうでしょうか?

頭の中のシミュレーションとそれを現実に適応した場合とでは往々にして違うことが起きます。

そもそも、みんなの頭の中で考えたことが完璧であればこの世にバグなど存在しないのです。

しかし現実のシステムはバグだらけですし「バグを減らす唯一の方法はコードを書かないこと」と言われるように、コードが存在すればその時点でバグの存在は否定できないものとなります。

それなのに自分の能力を過信して「このロジックで完璧だわ」と実際に動かしもせず、テストも書かずにコミットしてしまい、レビューや運用の段にになってはじめて初歩的なチョンボが発覚し、足をすくわれてしまうのです。

それだったらまだ、答えのアルゴリズムがパッと思い浮かばなくても、試行錯誤をしながら、探り探りロジックを構築していくほうがプログラミングとして正しい姿勢のような気がします。

プログラマーの入社試験でよくFizzBuzzのアルゴリズムを書かせる試験がありますが、個人的にこの試験ではプログラマーの適性は見抜けないと思っています。

試験時は実際にコードを書いて動かしながらロジックを書くのではなく、頭の中で考えたアルゴリズムをそのまま発表してもらって合否を判断するので、試行錯誤しながらコードを書き上げていくタイプの人間だと、この試験で落ちてしまいます。

ところで、英語でのalgorithmは

a set of rules that must be followed when solving a particular problem

定義されています。

アルゴリズムはルールを考えることと同義なのです。

ルールは実際にやってみて検証してみないと、それがほんとに効果があるかどうかは分かりません。

経済学がいくら発展しても一向に世界の景気を良くすることができないように、言説がいくら確からしくても、現実に適用できなかったり、できたとしても思っていた効果は得られなかったりするのです。

ですのでアルゴリズムを考える能力よりも仮説検証を繰り返しながらアジャイルのようにアルゴリズムを組み上げるやり方のほうが現実に沿っているのです。

そして、既知のアルゴリズムのほとんどはライブラリ化されていますし、ググれば大抵のものは見つかります。

よって、知識としてアルゴリズムを覚える価値もあまりないような気がします。

入社試験で出てくるもう一つ有名なアルゴリズムにバブルソートがあります。

これも「なんか隣と隣で比べながら並び替えていくんだな」ぐらいの認識で、実際の正確なアルゴリズムまでは覚えておく必要はないと思いますし、実際に「今書いてみろ」と言われてもサッと一発では書けません。

というわけで、文頭に考えていたアルゴリズム脳のデメリットは

  • 脳内思考に頼りすぎると仕様の矛盾に気づきにくく確認不足による間違いが増える
  • 脳内思考と知識としてのアルゴリズムを実装能力そのものと勘違いしてしまう

の2つになります。

実際の実装能力(=アルゴリズム構築能力)で大事なのは試行錯誤の繰り返しによる動作検証で、これはなかなか試験では見抜けない気がします。

Tag: プログラミング