プログラミングに必要な考え方

プログラミング教室が流行っています。大人向けだけでなく、子供向けにもプログラミングを教えていて、ブロックで簡単にプログラミングできます、といったものが多くあります。ただ、多くのプログラミング教室は小手先の技術や内容を教えるだけで本質的なところは教えてくれません。子供向けには「モノづくりが楽しい」という体験を教える方が大事なのでそれで良いのですが、業務でプログラミングをしようという人はそういうわけにはいきません。
以下に閏年の判定プログラムを例にプログラミングにはどういったことが必要なのかを解説します。

閏年判定プログラム

プログラミングを勉強したことがある人は必ず一度は作ったことがある問題として「閏年判定プログラム」があると思います。下記のような問題です。

西暦が4で割り切れる年は閏年とする。ただし、100で割り切れる年は平年とする。ただし、400で割り切れる年は閏年とする。xに西暦が与えられたとき、閏年の場合はTrue、平年の場合はFalseを返す関数を作りなさい。

いろんな人にプログラムを教えてきましたが、ここで挫折する人がかなり多いように思います。また、プログラムが書ける人でも微妙なコードを書く人は多いです。例えば下記のようなコードです。

def isLeap(x):
  if x % 4 == 0:
    if x % 100 == 0:
      if x % 400 == 0:
        return True
      return False
    return True
  return False

Pythonで書くとこういう例になります。これはちゃんとした閏年を返すの全然正解なのですが、個人的にはこのコードは単純に読みにくいです。

読みにくさ

このコードを渡されると、まず1行目を読み「なるほど、4で割り切れるときの処理なんだな」と思いますが、次に「100で割り切れる場合に限定するの?ANDを使わないで?」となって、「更に400で割り切れるとTrue?そうでなければFalse?更にそうでなければTrue?あれ?そのときの条件ってなんだっけ?」という具合に頭がついていきません。
ソースコードを読むときはできるだけ条件や今の状況を覚えておきたくないものです。実際はこの程度であれば問題ないですし、記憶力が良い人なら難なく読めるのでしょうが、業務ではもっと複雑怪奇な条件処理を行う必要があったりします。この手のソースを書く人はそういったときにかなり難読なコードを書く傾向にあります。
「読みにくいかどうかは個人の主観なので動けば問題無いでしょう」という人もいるとは思いますし、そういう考え方も間違いではないとは思いますが、以下に説明する全体の集合や論理的な考え方をしていないと追加開発やコード移植の際にバグを混入しがちで、のちに技術的負債となって大きな問題を引き起こします。

ChatGPTの回答は?

参考までにChatGPTに聞いてみました。

ChatGPTの作る閏年判定プログラム

これは非常に分かりやすいですし読みやすいですね。なぜこのようなコードが読みやすいのかを以下で解説します。

条件の集合を考える

この手のややこしい条件が与えられたとき、まずは落ちついて全体の集合を考えましょう。与えられる数字は西暦の数値なので基本的に自然数です。

全ての西暦の集合

この中から4で割り切れる西暦が閏年となります。
下の図のオレンジの部分が閏年です。

4で割り切れる閏年の集合

さて、では次の100で割り切れる西暦ははどこに位置するでしょうか。100で割り切れる数字は4でも割り切れるので集合としては内部に存在します。

100で割り切れる西暦は閏年ではない

最後に400で割り切れる数字ですが、これも4で割り切れますし、100でも割り切れます。なので一番内部に存在することになります。

400で割り切れる西暦は閏年

これで閏年の集合は完成です。このオレンジ色の部分こそが閏年となるのです。この集合を考えながらプログラムを作っていきます。

条件を簡単にしていく

この集合のオレンジの部分を取り出せばいいのですが簡単ではなさそうです。そういった場合は一度に処理することは諦めて順を追って簡単にしていきます。

一つずつ殻を剥いていきましょう。本来ならばまずは自然数にしなければならないのでマイナスや小数、虚数などは除きたいところですが割愛します(実際にはチェックすべきですがエラー処理になります)。次に、自然数の中で4で割り切れる西暦が閏年ということは4で割り切れない西暦は平年です。

def isLeap(x):
  if x % 4 != 0:
    return False

西暦のうちの4分の3は平年なので、その場合にさっさとFalseを返してしまうというのは性能にも微妙に影響します。これで残りの処理は「4で割り切れたときにどうするか」に集中できます。残された集合は次のようになります。

同様に次は100で割り切れなければ閏年です。

def isLeap(x):
  if x % 4 != 0:
    return False
  if x % 100 != 0:
    return True

これも25回に1回しかない100年で割り切れるときの処理を後回しにします。大半の4で割り切れる西暦は閏年なのです。残されたのは次のような集合です。

さて、最後は400で割り切れるかどうかの条件分岐です。

def isLeap(x):
  if x % 4 != 0:
    return False
  if x % 100 != 0:
    return True
  if x % 400 != 0:
    return False
  return True

400で割り切れなければ平年です。最後に残されたのが400で割り切れる場合なので閏年となります。
これで完成です。ChatGPTの出力と同じく、読みやすくなりました。

問題文によって大きく変わる

恐らくですが最初の問題文を聞かされたときに、読解力の高い人であれば
「要するに4で割り切れなければ平年なんでしょ?」
という逆の事実に気付けると思います。そこから順に考えていけば難しいことではないのです。例えば以下のような問題文であればどうでしょうか。

西暦が4で割り切れなければ平年である。4で割り切れて100で割り切れなければ閏年である。また400で割り切れても閏年である。xに西暦が与えられたとき、閏年の場合はTrue、平年の場合はFalseを返す関数を作りなさい。

この問題文を与えると、多くの人は4で割り切れない場合から処理を書いていきます。結果的にはシンプルな実装にすぐに辿り着くのです。ただ、実際にはこのようなプログラミングに優しい形で問題文が提供されていません。

実際のプログラミングでは問題が用意されていない

実際の業務などで与えられる条件や要求事項などはこのような問題文になっていませんし、そもそも整理されていません。バラバラに点在していますし、条件が明文化されていないこともかなり多くあります。加えて隠れて内在していて後から発覚するような条件もあります。そういった条件や状況に対して、ただ淡々とIF文を追加していくようなプログラミングをしていくと複雑怪奇なプログラムができあがり、処理が遅くバグも内包しているのにメンテナンス性が低くて誰も直せないような状況に陥ります。

例えばスーパーの商品にQRコードが貼ってあるとして、そこに値段や産地、消費期限やアレルギー情報などが書いてあるとしましょう。QRコードを読み取って下記のようなアプリを作りたい、という要望があったとします。

QRコードを読み取ってカゴに入れると合計が分かるようにしたいんだよね。クーポンが適用できる場合は適用した合計金額を出したい。割引シールが貼ってあればそれも加味したいね。アレルギーに関する情報があればアラートを表示させて、消費期限が切れてる場合も注意喚起しよう。ユーザーによっては消費期限が3日前ならアラートが出なくていいってのもあるだろうから、ユーザーの設定画面でその日数を入れて置いて、それをもとにアラートを出そう。あとは産地で国産か外国産かも判別させようか。

こういったフワッとした要求から、条件を絞り出していくという作業が非常に大事になります。この情報だけでも「アレルギーに関するものは優先的に出した方がいいのではないか」という考えになりそうです。商品をどのような集合として捉えてどのような処理を行うか、プログラミングの大半はそこにあるのです。

ウォーターフォールなのかアジャイルなのか

余談ですが、こういった条件の洗い出しや設計などはウォーターフォール開発で行われる要件定義や設計に関する部分になります。よく勘違いされるのですがアジャイル開発ではこの手の設計を無視して良いわけではありません。
アジャイル開発であってもシステムの根幹に位置するような変更が難しい部分や、特にサーバとやりとりするような変更に対するコストが高い部分に関しては綿密に設計します。設計した上でプロトタイピングしたものを作って「動くコード」をベースにするというところが大事であって、パワポの設計図に頼らないというのがアジャイル開発の重要なことの1つです。

また、新しい条件が出てきたときに設計から見直すことも視野に入れておきます。その際に使い回せる部分が多いように部品化しておいたり、依存関係を極力減らすような作業が大切になります。最初から完璧なものを作ることは目標にしませんが、後から使えるようにしておくような最低限のことはやっておく必要があります。

どこで学べるのか

こういったプログラミングに必要な知識はどこで学べるのでしょうか。例えば集合に関する知識は集合論として学べます。ベン図などは高校でも書いたと思います。加えて論理演算やブール論理などは大学で学ぶことができます。状態遷移図やデータベースの正規化なんかも非常に大事です。こうしたものは基本的に情報系の大学に行けば履修することができます

また、閏年の判定プログラムでは簡単な知識で解けますがオブジェクト指向のようにデータと処理を整理する場合の考え方や、そもそもの計算機知識などが必要な場面もあります。ネットワークに関する知識が無ければ通信するようなプログラムを書くときに非常に苦労するでしょう。例えばAPIからfetchするときにエラーが返ってきたとして、エラーに応じてどのような処理をすべきか、というのはインターネットの思想に関しても知っておく必要があります。こういった知識も基本的に情報系の大学に行けば履修することができます。

では情報系の大学に行けば一人前のプログラマーになれるでしょうか?いろんな人を見てきた限り、必ずしもそうではありません。知識として知っていても使えるかどうかは全く別物です。実際に、有名な大学の情報学部を卒業した社員にプログラミングをさせたところ、残念なコードが大量に出てきた、ということは多々あります。ただ、そういう社員には「こういう理論があったよね?」「ベン図を書いてみた?」と聞けば「そういえば習いましたね」となるのですぐに戦力となります。一方でプログラミングスクールや、学生時代にまともに授業を受けてこなかった学生だとそうはならないので、一から情報理論を学んで貰わないといけません。つまり、情報系の大学に行き、ちゃんと勉強してくる、という当たり前のことが非常に大切となります。


この記事が気に入ったらサポートをしてみませんか?