プログラミングは論理的思考の訓練になるか - 「プロになるJava」ボツ原稿

プログラミングと論理的思考の関係、「プログラマに大切なのは日本語だ」の実際に意味するところの話です。
「プロになるJava」でページ数などの関係でボツにした原稿で、結構ちゃんと書いたのですが、この先に日の目を見る機会もなさそうなので公開します。

4/8補足:こういう章を入れようとした背景としては、論理的思考とかロジカルシンキングとかはすでにマーケティング用語になっていて、主に情報整理術を扱う本にこういう言葉が使われていることが多いので、そういうマーケティング用語として使われてる無定義なものではなく、論理学を勉強しようよという狙いでこういう話題を含めようとしたのでした。

論理演算子

排他的論理和はあとの話題にも出ますが「プロになるJava」で扱ってないのと、これもボツ原稿なので、まず紹介。

排他的論理和

日常会話でも「または」と言うことがありますが「りんご、またはオレンジ」と言ったときには「りんごかオレンジのどちらか一方」になります。論理和の場合には両方trueでも成り立つので注意が必要です。
日常会話の「または」は「xor(えっくすおあ)」という論理演算になり、Javaでは「^」で表します。

どちらか一方だけtrueの場合にtrueを返します。

jshell> true ^ false
$1 ==> true

両方trueや両方falseの場合にはfalseを返します。

jshell> true ^ true
$2 ==> false

この演算は!=と同じ結果になるので、実際のコードでは!=を使うほうがわかりやすいでしょう。
表にまとめると次のようになります。

true false
true false true
false true false

この演算を排他的論理和といいます。

プログラミングは論理的思考の訓練になるか

論理的思考とはなにかという話にはいろいろな見方がありますが、ひとつに「正しく推論ができる」ということがあります。推論というのは、なにか情報が与えられたときに、その情報を組み合わせて結論を導き出すことです。

メソッド呼び出しと論理の関係

プログラミングと論理的思考の関連を考えるひとつの材料として、メソッドを組み合わせてプログラムを実装することと論理的な推論が対応しているという理論があります。

ここで論理的な推論というのは次のようなものです。 次のふたつの前提があるとします。

  • サカナは泳ぐ
  • マグロはサカナである

このふたつの前提から次の結論を導きだします。

  • マグロは泳ぐ

この推論とプログラムの関係を考えてみましょう。 「XはYである」を「型Xを引数にとって型Yを返すメソッドがある」を置き換えます。 そうすると、最初の前提条件「サカナは泳ぐ」「マグロはサカナである」は次の2つのメソッドp1とp2になります。

Oyogu p1(Sakana s);
Sakana p2(Maguro m);

そして結論「マグロは泳ぐ」は次のメソッドc1になります。

Oyogu c1(Maguro m);

この結論が2つの前提条件から導きだせることと、メソッドc1がメソッドp1、p2を使って実装できることが対応しています。 ここでc1は次のように実装ができるので、先ほどの推論も妥当だということになります。

Oyogu c1(Maguro m) {
  Sakana s = p2(m);
  return p1(s);
}

間違った推論の例も見てみましょう。 次の2つの前提があるとします。

  • サカナは泳ぐ
  • イルカは泳ぐ

この2つの前提から次の結論が導き出せるか考えてみます。

  • イルカはサカナである

前提「サカナは泳ぐ」はメソッドp1として使いまわせるので、「イルカは泳ぐ」に対応するメソッドp3を宣言します。

Oyogu p1(Sakana s);
Oyogu p3(Iruka i);

結論「イルカはサカナである」に対応する次のメソッドが、2つのメソッドp1、p3を使って実装できれば、この推論は正しいということになります。

Sakana c2(Iruka i);

このメソッドを実装するには、Sakana型を返すメソッドが必要になりますが、今回の前提条件にはありません。そのため、このメソッドc2はメソッドp1とp3を使って実装できないということになります。
このことが、「サカナは泳ぐ」「イルカは泳ぐ」という前提条件から「イルカはサカナである」という結論は導きだせないということにつながります。

このように、メソッドを組み合わせて実装できることと、前提条件を組み合わせて結論を導きだすことは同じ形をしています。この対応はカリーハワード同型対応といいます。
このことを考えると、メソッドを組み立てて新しいメソッドを作ることは論理的思考の練習にもなってるということになります。その点では、プログラミングは論理思考の訓練になっていると言えます。

間違った推論から導いた間違った結論をもとに行動してしまうと、損してしまうことになります。
また、結論が間違っている場合に、推論が間違っているのか前提が間違っているのかわからなくなります。

命題論理

プログラマに必要な言語は日本語だ!」「まず日本語を勉強しろ」のような発言を見ることがありますが、日本語で日常生活を送れているのであれば、それ以上に日本語を勉強するというのは、漢字をたくさん覚えるとか「すべからく」で始まる文は「べし」で終わるというような係り受けを正しく使うとか、小説をたくさん読んで表現を覚えるということになります。
でも、プログラマには日本語が必要といっている人の求めているのは、そういった日本語能力ではありません。
実際には、文章の論理構造を読み取れるようになってほしいとか、文章を書くときの論理構造を正しく扱えるようになって欲しいということを求めています。
そのときに勉強しないといけないのは論理学ということになります。

日本語の「または」の論理構造が実際にはあいまいであることを「排他的論理和」の説明で示しました。
ほかには「ならば」もあいまいです。たとえば「晴れならば遊びにいく」というとき、日常会話では「雨のときは遊びにいかないんだな」と思いますが、実際には雨の場合はなにも言っていません。晴れのときに遊びに行かなかったら、この文は成り立たなかったことになりますが、論理構造としては雨の日は遊びにいってもいかなくても「晴れならば遊びにいく」という文は成り立つことになります。
説明や議論のための文章を書くときには、こういった実際はあいまいな論理になるような文を使ってしまわないことが大切です。

Twitterでよくみられるのが「晴れならば遊びにいくと言ったのに雨でも遊びに行ってるじゃないか」のような、「ならば」を誤読したそこに書かれていない条件を基準にした批判です。
こういった論理構造からはずれるやりとりを防ぐためには、文章の論理構造を式にして確認する練習を一度やっておくといいと思います。

「AならばB」を論理式として考えると、AがtrueのときにBがfalseの場合だけfalseになる式ということになります。Javaに該当する演算子はありませんが、記号としては→を使います。
表にすると次のようになります。

A B A→B
True True True
True False False
False True True
False False True

ここで、「サカナは泳ぐ」は「サカナならば泳ぐ」と言い換えることができます。ふたつの前提条件がどちらも成り立つということは「且つ」でつなぐことができます。また、前提条件がなりたつ「ならば」結論がなりたつということになります。
まとめると、さきほどの推論の例は次の論理式で表せるということになります。

(サカナ→泳ぐ && マグロ→サカナ) → (マグロ→泳ぐ)

この「マグロ」「サカナ」「泳ぐ」それぞれが成り立つとき成り立たないときのすべての組み合わせについて、式を確認していくと次のような表になります。

マグロ サカナ 泳ぐ サカナ→泳ぐ マグロ→サカナ & マグロ→泳ぐ 結論の「→」
True True True True True True True True
False True True True True True True True
True False True True False False True True
False False True True True True False True
True True False False True False True True
False True False False True False False True
True False False True False False True True
False False False True True True True True

これを見ると、結論はすべてTrueになっているので、どのような値の組み合わせでも成り立つことになります。常にTrueになる式をトートロジーといいます。推論を論理式に置き換えたときにトートロジーになるとき、その推論は正しいといえます。そのため、この推論は正しいということができます。

間違った推論の例を論理式で表すと次のようになります。

(サカナ→泳ぐ && イルカ→泳ぐ) → (イルカ→サカナ)

この論理式についてすべての組み合わせを確認すると次の表のようになります。

イルカ サカナ 泳ぐ サカナ→泳ぐ イルカ→泳ぐ & イルカ→サカナ 結論の「→」
True True True True True True True True
False True True True True True True True
True False True True True True False False
False False True True True True True True
True True False False False False True True
False True False False True False True True
True False False True False False False True
False False False True True True True True

今度は結論にFalseがあることがわかります。つまりこの論理式はトートロジーになっていません。そのため、この推論は正しくないということになります。(※イルカでありサカナではなく泳ぐというときFalseになっています)

こういった論理は命題論理といい、正しい説明を書くためには必要な考え方です。
いくらJavaを使いこなせるようになって高度なプログラムが書けるようになっても、作ろうと思うもの自体に誤りがあっては、正しいプログラムは作れません。
ぜひ、論理学を勉強してみてください。

補足

ボツ原稿はここまで。 プログラミングに論理が強く関係してそうなことがちょっと見えたんではないかなと思います。論理学は数学の一分野でもあるので「プログラミングに数学は必要か」という話の答えでもありますね。

あとは、論理学の参考書籍を

いきなり「論理学」と言うと抵抗のある人には、文章の論理構造から論理学に入門するこちらの本がいいと思います。
プログラマはまず日本語を勉強すべし」と言ってる人が勉強させたいものは、この本のようなことだと思います。

もうすこし形式的に論理学を勉強する場合はこちらを。パラドックス不完全性定理など、難しめの話題も取り扱っていますが、まずは述語論理まで読めば十分かと。

プログラミングとの関係はこちらが。論理を拡張していくことで関数型プログラミングになっていくことが説明されています。カリーハワード同型対応のちゃんとした説明もこの本にあります。