100から1まで表示するコードすら書けないプログラマ:約14%

愚痴(泣き言)とコーディング能力の相関関係を調べたデータがあるのだが、それによれば、愚痴の時間が長かった人ほど、ごく簡単な問題を解くのにかかった時間が長かったそうだ。

「簡単な問題」の内容は「100から1まで表示する」という、FizzBuzzよりも遥かに簡単な内容なのだが、この調査においては、14%の人が10分経っても解くことができなかったという。おそらく母数が22人で、22人のうち3人が解けなかったということだろう。

↓の記事のグラフを見てほしい。

Easier Than Fizz Buzz - Why Can't Programmers Print 100 to 1?

調査(というか実験)は2ヶ月間にわたり、22回の面接にて行われた。こんな感じの内容を採用面接に組み込んだようだ:

  1. 現在の所属組織/マネージャの好きではないところや、不満な点について何点か話してもらう。
  2. 「100から1まで表示する」というプログラミング問題を解いてもらう。
    • ただし、「100から1まで表示する」というアルゴリズムの中核部分は「for(int i=0;」というコードから始まるとする。これより前に「100から1まで表示する」アルゴリズムに関係するコードを書いてはならない。
    • ループを2つ使うのもNG。

C++でいうなら、こんな感じの問題だろうか? コメント「ここから」から「ここまで」の間にコードを書き加えて、「100から1まで表示する」処理を完成させる。

#include <cstdlib>
#include <iostream>
// 上記以外の必要なヘッダは自動的にインクルードされるものとする

int main()
{
    // ここから
    for (int i = 0;

    // ここまで
    return EXIT_SUCCESS;
}

実際の言語としてはC、C++C#Javaあたりだろうか? 内容的にJavaScriptPerlPowerShellにも流用しやすい気がする。

調査の結果、得られた集計データはこんな感じ:

  1. 14%の人は、10分経っても問題を解けなかった(打ち切って、次の質問に移った)。
  2. 40%の人は、問題を解くのに5分を超えたか、2回以上間違えたか、その両方だった。
  3. 2分以内に解けたのは14%だけだった。
  4. 82%の人は、問題が解けるまでに1回は間違えた(つまり1回では解けなかった)。

この調査から、愚痴(泣き言)と能力の間に相関関係が見られることは確かだが、因果関係については不明なままだ。仕事に必要なスキルを持ち合わせていないから愚痴を言うのか、それとも愚痴っぽいがために仕事に必要なスキルを持ち合わせていない状態に至っているのか、この調査からは不明だ。*1

ちなみに私も解いてみた。解答はこんな感じ。

#include <cstdlib>
#include <iostream>

int main()
{
    for (int i = 0; i < 100; i++) {
        std::cout << 100 - i << std::endl;
    }
    return EXIT_SUCCESS;
}

解き方はすぐ分かったのだが、実際に問題なく動くコードを書き上げるのに大体2〜3分ぐらいかかり、その最中に2回間違えた。1回目は誤って「for (i = 0;」と書いてしまってコンパイルエラーとなった。2回目は「100 - i」をなぜか「100 - 1」と書いていてカウントダウンされていなかった(本人は「100 - i」と書いたつもりだったのだが……いかん、腑抜けているな)。

なおC++による意味のないデラックス豪華版の解答(完全に本来の趣旨から離れている……):

#include <algorithm>
#include <array>
#include <cstdlib>
#include <functional>
#include <iostream>
#include <numeric>

int main()
{
    std::array<int, 100> ary;
    std::iota(ary.begin(), ary.end(), 1);
    std::sort(ary.begin(), ary.end(), std::greater<int>());
    std::for_each(ary.begin(), ary.end(), [] (const int n) {
        std::cout << n << std::endl;
    });
    return EXIT_SUCCESS;
}

こちらは7〜8分かかった。ネット上の標準ライブラリのリファレンス(英語)を参照しながら書いたからなあ。ただし1発で問題なく動作した。

で、本筋と離れたところでふと思ったのだが、最近の言語って数値のシーケンスを簡単に作れるから、その影響で「1〜100までのシーケンスを――」という発想の方が先に出てくる人も結構いるのかもしれない。

例えばRuby

(1..100).sort{|a, b| b <=> a }.each{|n| puts n }

例えばPython

for n in range(100, 0, -1):
    print n

例えばCoffeeScript

console.log n for n in [100..1]

例えばScheme

(for-each print (iota 100 100 -1))

例えばbash

printf %s\\n {100..1}

例えばシェルスクリプト

seq 100 -1 1

例えばPowerShell

100..1

また、私が最初に書いた解答をRubyで書き直すと、こんな感じになる。

100.times {|i| puts 100 - i }

0.upto(99) {|i| puts 100 - i }

「100 - i」というアイデアこそ同じものの、構文は随分と異なる。というかよく訓練されたRuby使いならfor文よりもInteger#timesやInteger#uptoを使うだろう。

さらに言えば、よく訓練されたRuby使いはInteger#downtoを使うかもしれない。

100.downto(1) {|i| puts i }

こうして見てみると、言語によって抽象度・よく使う構文・適切なアプローチが異なるので、言語Aのユーザにとって簡単な問題が言語Bのユーザには簡単ではない、ということが起こりうるから、調査の際には気をつける必要がありそうだ。シェルスクリプトPowerShellでは、アルゴリズムを考えずに1文書くだけで実現できてしまうからなあ。

まあ、件の調査は会社の採用面接で行ったようだから、書類選考の段階で「C/C++/C#/Javaでの経験あり」の人に絞り込むとかしていると思われるので、その手の懸念は不要だろう。

*1:個人的には、愚痴っぽくなる程度にストレスフルな環境にいることで、スキルを広げる/高める意欲を失ったり時間を得られなかったりしている可能性だって考えられるよね、と思う。まあ、それ以前に、この調査が採用面接で行われたこと自体も何らかの影響を与えているだろうし……。