見出し画像

【プログラムのいろはの「い」】第1回:14年目のFizzBuzz問題”の問題”


FizzBuzz問題とは

FizzBuzz問題”の問題”をJeff Atwoodが提唱して、当時のプログラミング業界を騒がしてから14年が過ぎた。

FizzBuzz問題とは、以下のような問題である。

1から100までの数をプリントするプログラムを書け。ただし3の倍数のときは数の代わりに「Fizz」と、5の倍数のときは「Buzz」とプリントし、3と5両方の倍数の場合には「FizzBuzz」とプリントすること。

どうしてプログラマに・・・プログラムが書けないのか?

「FizzBuzz問題」でGoogle先生にお伺いを立てると、上記問題の解答となるプログラミングコードが多数出てくる。

何ならFizzBuzz問題のWikipediaにだって、FizzBuzz問題の解答プログラムは載っている。

わたしも試しにJavaでFizzBuzz問題のプログラムを書いてみる。

import java.util.*;

public class Main {
    public static void main(String[] args) throws Exception {
        for (int i = 1; i <= 100; i++) {
            boolean output = false;
            if (0 == (i % 3)) { System.out.print("Fizz"); output = true; }
            if (0 == (i % 5)) { System.out.print("Buzz"); output = true; }
            if (output) { System.out.println(""); continue; }
            System.out.println(i);
        }
    }
}

printとprintlnを使い分けたり、else ifを使わないようにして、若干トリッキーなプログラムになったが、剰余演算の回数を最小限に抑える工夫と思ってほしい。

このくらいであれば、何も見なくても3分くらいでかけるし、一発で動いた。
以下のJava実行サイトで動かしたら、上記のプログラムで間違いない出力が得られたので、これも一つの正解なのだろう。

この記事の主題ではないが、あくまでループ内で剰余演算をする方針は変えずに、ここから実行時の処理コストを減らそうとすると、ループ内の条件分岐処理をさらに減らして、以下の様に書くこともできる。

import java.util.*;

public class Main {
    private final static Runnable EMPTY_OUTPUT = () -> { System.out.println(""); };
    public static void main(String[] args) throws Exception {
        for (int i = 1; i <= 100; i++) {
            final int index = i;
            Runnable out = () -> { System.out.println(index); };
            if (0 == (i % 3)) { System.out.print("Fizz"); out = EMPTY_OUTPUT; }
            if (0 == (i % 5)) { System.out.print("Buzz"); out = EMPTY_OUTPUT; }
            out.run();
        }
    }
}

これでプログラム内の条件分岐は減ったが、ループの中でRunnableオブジェクトを毎回生成するので、処理コストはむしろ上がっている。
したがって、以下の様な修正により、最初の解答よりも処理コストを減らせるだろう。

import java.util.*;

public class Main {
    private final static OutputFilter NO_OUTPUT = (i) -> { return ""; };
    private final static OutputFilter COMMON_OUTPUT = (i) -> { return i; };

    public static void main(String[] args) throws Exception {
        for (int i = 1; i <= 100; i++) {
            OutputFilter filter = COMMON_OUTPUT;
            if (0 == (i % 3)) { System.out.print("Fizz"); filter = NO_OUTPUT; }
            if (0 == (i % 5)) { System.out.print("Buzz"); filter = NO_OUTPUT; }
            System.out.println(filter.filter(i));
        }
    }
    
    @FunctionalInterface
    private interface OutputFilter {
        public Object filter(int number);
    }
}

FizzBuzz問題は簡単な問題だが、同じ結果を得るプログラムの解答が三つできた。
これらは同じ結果を得るという点で機能的には同じだが、実行時にコンピュータが使うCPUやメモリ量に違いがある。いまどき、耳に入れるワイヤレスイヤホンのSoCであっても気にする必要のないような程度の差でしかないかもしれないが。
とはいえ、これだけでもプログラムの世界の奥深さが解るというものだろう。

ついでに、Pythonで少ない行数で書くとこんな感じ。

# coding: utf-8

for num in range(1, 101):
    print("FizzBuzz" if (num % 15) == 0  else "Buzz" if (num % 5) == 0 else "Fizz" if (num % 3)  == 0 else num)

しかし、この記事でもそうだし、FizzBuzz問題を提唱したJeff Atwoodもそうだが、プログラムの奥深さについてを問題にしたいわけではない。

ちなみに、このFizzBuzz問題について少しだけ補足をすると、職業プログラマではあまり使う機会のない剰余演算子を使う必要がある(使わない解答もあるが)ので、実際にFizzBuzz問題をプログラマに解かせようとすると、解けない人はそれなりにいると思う。

しかし、FizzBuzz問題"の問題"を提唱したJeff Atwoodは、剰余演算子を使いこなせるかどうかを問うているわけではない。
むしろ、Jeff Atwoodは「バカみたいに簡単(intentionally easy)」にしてあるはずの問題例に、あまり一般的には使われない剰余演算子を使わせるようにしたのは、彼のミスであるように思う。

FizzBuzz問題”の問題”

そもそも、Jeff Atwoodはこんな「バカみたいに簡単」なプログラミング問題を公表したかったわけではない。

これは、このFizzBuzz問題を取り上げたブログ「どうしてプログラマに・・・プログラムが書けないのか?Why Can't Programmers.. Program?)」の題名がすでに物語っているが、あえて内容の中でも重要な箇所を引用する。

私と同様、この著者は、プログラミングの仕事への応募者200人中199人はコードがまったく書けないということで苦労している。繰り返すが、彼らはどんなコードも書けないのだ
(中略)
FizzBuzzテストが簡単すぎるとは思わない方がいい(実際意図的にバカみたいに簡単にしてあるわけだが)。イムランのポストにコメントした人が、このテストの有効性について書いている。
面接者がFizzBuzzテストを簡単すぎると思ってやらないことを残念に思う。私の経験で言えば、最も簡単なプログラミングタスクさえできない候補者の多さには、まったく驚くしかない。
最初に候補者の書いたコードを見ることもなくプログラマを面接するというのが、そもそもばかげたことなのかもしれない。

どうしてプログラマに・・・プログラムが書けないのか?

これらを要約すれば、つまり、Jeff Atwoodは以下の問題を世に問うている。

プログラムを書けないプログラマが多い!

転じて、この問題は以下の重大な問題を提唱したと思われる。

どうすれば人間はプログラムを書けるようになるのか?

プログラムを「書ける」ひとにとって、この問題は問題にならないだろう。なぜならすでにプログラムを書けるのだから。
プログラムを書けるひとたちは、むしろこの記事の中の「200人中199人はコードが全く書けない」と書かれた点に注目して、俺はプログラムを書ける200人中1人の選ばれた人間だぞ、とFizzBuzz問題の解答を公開して誇るようになった。
あるいは、自分はプログラムを書けないプログラマかもと不安になって、FizzBuzz問題の解答を公開した。
そして、FizzBuzz問題の解答ばかりがインターネット上に溢れかえるようになった。

しかし、改めて述べるが、Jeff Atwoodが「どうしてプログラマに・・・プログラムが書けないのか?Why Can't Programmers.. Program?)」で訴えたかった事は「FizzBuzz問題っておもしろいだろ?」でも「プログラムを書ける人はスゲー」でもない。
彼が言いたかったのは「プログラムを書けない人が多くて困る」「プログラマの採用にはコードを書かせなければならないだろう」である。

この問題はプログラマ採用の選別方法の改善で一旦の解決を見るかもしれないが、プログラムを書けるプログラマが本当に200人中1人の割合でしかいないのだとすれば、どんな方法を用いても0.5%のあたりを正確に選別するのは相当の労力がかかるのは想像に難くないだろう。
できることならば、こんな選別をするくらいであれば、プログラムが書けないひとを書けるようにした方が良い、

すなわち、FizzBuzz問題”の問題”とは、以下の問題に帰結する。

どうすれば人間はプログラムを書けるようになるのか?

プログラマ業界の実態

私はプログラマ業界に入って20年以上になる。
すこしはプログラマ業界についてを語っても良いような気もする。

これはあくまで私見であることを断ったうえで、Jeff Atwoodがいうようにプログラムを書けないプログラマが本当に多いのかについて、少し語ってみたいと思う。

残念ながら、プログラマという職業についていながら、プログラムを全く書けないひとというのは、一定数いる。
200人中199人は言い過ぎだと思うし、世の中のシステム開発会社やシステム開発プロジェクトの質は玉石混交であるから、はっきりとしたことは言えないが、それでも、プログラマやSEを名乗る人の少なくとも3割はプログラムを書けないんじゃないかと思っている。

実際に様々な会社からプログラマを集めてチームを立ち上げる際、私はこのチームメンバーの3割はプログラムを書けないプログラマであろうと見込んでいて、当然ブレはあるが、大抵において正解だった。

プログラムを「書ける」「書けない」はイチゼロでは測れない部分もある。
サンプルプログラムを真似て同じようにプログラムを書けるのを、「書ける」としてよいのか?
マルチスレッドプログラミングを完璧に書けないと「書ける」とするべきではないか?
プログラムを「書ける」についての定義に疑問は尽きない。

しかし、一般的なシステム開発のプロジェクトにおいて、フレームワークと開発環境に加えて実際に動くサンプルコードを用意したうえで、仕様を聞いて詳細設計書を書き起こし、レビューでOKがもらえるプログラムとテストプログラムを書けるひとをプログラムが書けるひととすれば、やっぱり、現場にやってくるプログラマの3割程度はこのふるいから落とされるように思える。

彼らはなぜプログラムを書けないのか

数年前、全くプログラムを書けないひとと一緒に仕事をする機会があった。彼の名前をAとする。

Aはプログラムを書けないし、プログラムを書いてもらう以外の仕事もなかったので、仕方なく、Aには業務時間を使ってプログラミングを無料で学習できるWebサイトで問題を解いてもらうことにした。
このカリキュラムを終了すれば、Aでも多少はプログラムを書けるようになる、つまり仕事ができるようになると思えたからだ。

Aはそのカリキュラムを、ほとんど詰まることなく順調に進めることができた。
カリキュラムの内容は、一番簡単なHello Worldから始まり、条件分岐やループなどの記述例が最初に示されて、ほとんど例題と同じか少し応用を利かせた問題を解いていく形式であった。
たしかにこのカリキュラムをこなせば、プログラミング言語の文法を暗記することはできるだろうし、例題と同じ問題が出れば、その解答を出せるようになるだろう。

しかし、カリキュラムを終えたAは、実際の仕事においての能力は全く変わらなかった。
全く、である。

たしかに、プログラムを全く書けないひとというのは、基本的なプログラミング言語の文法すら覚えていないケースが多い。
しかし、彼らは文法を覚えていないからプログラムを書けないわけではないようである。
むしろ、プログラムを理解できないから、理解できないものへの記憶力が低下しているだけのように思える。
少なくとも、プログラマがプログラムを書けないのは、プログラミング言語の文法を知らない、覚えていないのが根本原因ではないようであった。


もう一人、私の部下として数年間一緒に働いたひとについても話したい。
彼の名前はBとする。

Bもプログラムを全く書けないひとだった。
プログラム以外の能力も総じて低かったように思える。
それでもシステム開発現場においておく以上、何かしらの仕事を与えなければならない。
試験期間にはテスト打鍵をさせるが、Bはそのテスト打鍵ですらも他の人より効率が悪かったりミスが多かったようである。

設計期間は設計書を見様見真似で書かせて、有識者に直させてどうにかやり過ごした。
実装期間は、画面デザインの修正をGUIツール上で直させる仕事だけをやらせた。
それしかできなかったのだ。
しかし、システムの画面というのは便利で解りやすいGUIツール上でマウス操作をするだけでは思い通りに直せないケースもある。
たまには画面の実装となるHTMLコードを直さなければならない。
BはHTMLコードすら全く理解できず、HTMLコードを指示通りに修正することはできなかった。

プログラムとは、コンピュータを動かす命令でもあるが、人間にもコンピューターにも理解できる共通言葉でもある。
Bが抱えている根本的な問題については、プログラミングの能力が低いというか全くない以前に、目に見える現実を言葉で表現する能力と、言葉が表現しようとした現実を想像して理解する能力が著しく低いように思えた。


AとBにとって、私は嫌な先輩、嫌な上司であっただろう。
しかし、私にとって彼らは、人間がプログラムをどのように理解するのか?の答えを見つけるヒントをくれた、貴重なひとたちであった。

プログラムのいろはの「い」

このシリーズでは「プログラムのいろはの「い」」についての説明記事を書いていきたいと思う。

しかし、プログラムのいろはの「い」とは何であろうか?

これは長い間、一般的にHelloWarld!を画面に出力することであり、それから変数、式、条件分岐、ループの文法を覚えるのが常道であった。
しかし、それをスタート地点にしてしまうと、上に挙げたプログラムが書けない人たちを見るに、多くのひとたちがプログラムの学習で脱落してしまうように思えるし、それは歴史も証明している。

日本の文部科学省か依頼を受けた学者が賢いのかはわからないないが、日本で導入されたプログラムのカリキュラムは多少の工夫がみられて、HelloWarld!の画面出力から始めるわけではないようだが、現場で生徒たちに教える教師たちがどれほどプログラム教育の意図を理解できているかは疑問がある。

これから始めるシリーズでも、プログラムの文法より前にプログラミングができるようになるのに必要な考え方を深掘りして書いてみたいと思う。

これは、Jeff Atwoodが14年前に世間に投げかけた真の問題、そして多くのひとが無視をした問題への、私の挑戦である。

どうすれば人間はプログラムを書けるようになるのか?

この問題に、はっきりとした正解を出せる自信はない。多分、無理なのだと思う。

しかし、やれるところまでやってみようと思うのである。

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