読者です 読者をやめる 読者になる 読者になる

Septeni Engineer's Blog

セプテーニエンジニアが綴る技術ブログ

エンジニアとしてもっておきたいロジカルシンキング入門

0. 前座

皆様、お久しぶりです。始めての方ははじめまして。エンジニアの原田です。
今回は一見すると、エンジニアと関係ないようなテーマを上げました。

しかし、プログラムの原点はロジック(アルゴリズム)。
頭が整理できていないとコードも整理できていない。
一度、原点の思考に立ち返って考えてみようと思いました。

1. なぜロジカルシンキングが必要になるのか?

http://d.hatena.ne.jp/masatoi/20090707/1246965336
原文はこちら「http://blog.codinghorror.com/separating-programming-sheep-from-non-programming-goats/

聡明な先輩方はこの記事の存在を知っていると思います。
詳細はご自身で読んでいただきたいのですが、かいつまんで結論を1行で表現すると。

「プログラムに必要なのは、抽象概念を抽象的な状態で表現できる能力が必要」

という話です。
また、実例として最近の僕の開発の中では、業務を抽象化してそのままコードに落とすといったことをやっています。(DDD ドメインドリブンデザイン)
業務という具体の中にも抽象化すると言った行為は必ず必要となってきます。
それは、つまりは 「業務の本質(目的)をロジカルに導き出す」というプロセスがあるわけです。

な の で ロジカルシンキング が 必要不可欠」
という考えになりました。

2. 基礎となる2つのロジック

2-1. 帰納法

(前提)
ウサギAは白かった。
ウサギBは白かった。 
ウサギCは白かった。

(結論)
つまり、ウサギは白い。

自らの経験・前提を元に汎化する方法です。
しかしながら、この帰納法には致命的な欠陥があります。
それは、未だ見ぬウサギDは黒いかもしれないという可能性です。

この方法では、必ずしも前提が正しいから結論は正しいという保証ができません。
では、論理保証がされないこの方法は何に使うのか?

それは僕は「推論」と考えています。(言い換えると仮説立て)

実務として落としこむと問題解決の仮説立てなどで役に立つでしょう。

Class Aを継承したBがバグっていた。
Class Aを継承したCもバグっていた。

(仮説)
Class Aを継承しているものはバグっているのではないか?
(検証)
Class Aを継承したDも確認しよう。

2-2. 演繹法

(前提)
AはBである。
BはCである。

(結論)
故にAはCである。

こちらは帰納法と異なり、前提が正しければ完璧な理屈となります。
しかし、こちらにも弱点があります。
それは前提条件が誤っていた場合には、必ず誤りを導くということです。

少し具体的にすると、
帰納法で求められたAとBから結論Cを導いたとします。
前述のとおりAは、仮説なので誤っている可能性はあります。
そして、検証した結果Aは誤りと判明した場合には、芋づる式にCも誤っていることになるということになります。
(もちろんBが誤っていた場合も同様です。)

こちらは実務としては、仮説の強固な裏付けに向いていると僕は考えます。

Class Aを継承したBがバグっていた。
Class Bを継承したCもバグっていた。

(仮説)
Class Aはバグっているのではないか?
何故ならば、CのベースとなるClass Bの更にベースになるクラスはAであるからである。 <= コレ

3. 応用概念

3-1. 限定条件

2−1項で、帰納法は前提が正しければ、結論は正しいと保証されないといった話をしました。
しかし、限定条件をつけることでこの帰納法で導かれたロジックを前提も正しい、結論も正しいといった状況にすることができます。
(下の例をみるとずるいと思うかも知れませんが…

例)
(前提)
ウサギAは白かった。
ウサギBは白かった。 
ウサギCは白かった。

(結論)
つまり、ウサギは白い。ただし、ウサギA、B、Cいずれかに限る

そう、見たもの、前提だけで絞り込めば、帰納法も全ての場合が網羅されている「完全帰納法」という形になります。
完全帰納法になれば、前提が正しければ、必ず結論も正しいことが保証されるのです。

3-2. 対偶の利用

中学校でならった対偶による理論の転換です。
逆の概念を証明するときに利用します。

実務的なところで言うとバグの影響範囲説明などで使用すると良いと思います。

例)
Class Aを使用した機能は全てバグA'が生まれる。

対偶
バグA'が生まれない機能はClass Aを使用していない機能である。
つまりは、バグA'が含まれていないものはClass Aとは無関係といえるでしょう。

3-3. ピラミッドストラクチャー(ロジックツリー)

端的にいうと2章で説明した2つの基礎論理を組み合わせて深堀りの構図のことを言います。
階層を重ねるごとにロジックは強固になり、外圧的な否定に対して強くなります。

実践では、次のようなときに有効です。
上司を説得しようとすると、大体突っぱねられる場合にはさらに階層を深くしてより強いロジックツリーをつくることで強固な説得が可能です。
さらにいうと、3−1の限定条件をつけて完全なロジックを作ると更に有効でしょう。

4. Append抽象論への落とし込みの実例(要求分析)

要求「1日の終わりが分かるように定時でなるアラームを作りたい」

分解と定義

定時 => ユーザーが設定した時間である(経験に基づく帰納法による仮説)
アラーム => ユーザーに通知する機能である(経験に基づく帰納法による仮説)

演繹による結合
ユーザーが設定した時間にユーザーに通知する機能がつくりたい(結論)

(ここからAppend 次回の記事でここやるかも?)
抽象への抽出
設定した時間 => SettingTime
ユーザー => User(必要ないかもしれないけど)
通知 => Alert

DDDへの落とし込み
User#SetTime
System#PushAlart

5. 最後に…

実例とつなぎこんでより実践的にしてみましたがいかがだったでしょうか?
たんたんと書きましたが、これらって実は中学校までの義務教育でならったことなんですよねー。

当たり前のことじゃん!そうです。中学校まで履修していれば当たり前なんです。
しかし、複雑な要件や複雑なストーリーを前に目が曇ってしまうのが人間だと僕は思っています。
なので、もし目の前の複雑なことにハマってしまっている(行き詰まってしまった)場合には一度原点に戻ってみてはいかがでしょうか?

全体を俯瞰して、単純化すればそこに解決の糸口は見えるかもしれません。