FizzBuzz問題

どうしてプログラマに・・・プログラムが書けないのか?(原文Coding Horror: Why Can't Programmers.. Program?)のFizzBuzz問題をわが情報教育課程3年生の優秀な学生42人に制限時間付きでC言語で解かせてみた。結果はそのうちどこかで。この話をしていたら天才なかの先生が「いったん書いて消す」というヒントをくださったのでやってみた:

#include <stdio.h>

int main(void)
{
    int i;

    for (i = 1; i <= 100; i++) {
        printf("%d\r", i);
        if (i % 3 == 0) printf("Fizz");
        if (i % 5 == 0) printf("Buzz");
        printf("\n");
    }
    return 0;
}

ただし上書きすると前のが消える端末専用。

このくらいの量だとしても、2分で書き上げろというのは

かなり難しいんじゃないでしょうか?

そうですね

最速の学生でもエディタで作ってコンパイルして実行して確認してからMoodleに書き込むのに5〜6分かかっています。

>ただし上書きすると

>ただし上書きすると前のが消える端末専用
そういう意味ですか。
なんでこれでいいのか1~2分悩んでしまいました。

なぜ書けないのか理由を考えてみました。
1) 単に C を知らないだけで Java/Perl/Shell Script 等なら書ける
2) 問題が「30分で 10 言語以上で書け」だった
3) TSS環境でみんながフルスクリーンエディタを立ち上げるので1行書くのに1分以上かかる
4) 問題が英語で、それを理解出来なかった学生が(ry

意図的な誤読ですか?

件の和訳でも確認できるのですが、「実行せよ」とは言っていません。
「2分とかからずに紙に書き出せる」とあるだけです。
つまりは動くかどうか、バイナリが作れるかどうかをコンパイラを通さねば確認できないような初学者は、その段階で足切りされているということです。

私もやってみたら、3分かかりました。ただ、最初の一分越えたあたりで、高速化を図るには……と関係の無い小道に思考が舞い込んでしまったためでもありますが。
個人的にも、2分はかなり厳しいラインかと思います。

2分?

原文を見るとa couple of minutesとなっています。2分というよりは2〜3分または数分ですね。

初めから2分と言われれば

さっさと4分岐のコードを書きますね。
そうではなくて問題だけ出されたら考えるだけで2分経過してしまうかも。

Re: 初めから2分と言われれば

ですね。私もたぶん単純な4分岐を書きます。
学生のを見ていると,できていないものは4分岐しようとしながらelseでつないでいなかったり,順番がおかしかったり。nekuraiさんのおっしゃるように言語がわからないのではなく論理のレベルで墜落しています。

instead of the

instead of the numberとあるので、
FizzなりBuzzなりのときは数字は出力しない仕様ですね。

Re: instead of the

はい,そうです。上のプログラムがそうなっているのはちょっとわかりづらいかと思いますが \n ではなく \r を使っているのがミソです。

復帰/改行

CR/LFが必要なTTY端末は珍しくなったのでしょうかねぇ。
 OKIタイパーで学生時代を過ごしてました。カタカタとけなげにタイプヘッドが移動していくのをみているとかわいく感じたものです。
 XとWとを重ね打ちして黒っぽさを表現したりして、今とは違ったアスキーアートを書いていたのが懐かしい。

Re: 復帰/改行

たいていの「端末」(コマンドプロンプトとかMacのターミナルとか)で \r で行の先頭に戻りますよね。

Re: 復帰/改行

で、問題が「1から100000までの数」に変わったら破綻するわけで…^^) 出力文字列のバッファを上書きするのなら許すかな。
ついでに「3と5両方の倍数」の場合に出力する文字列がそれぞれの倍数の場合の文字列の連結になっているのが本質なのかどうか、悩ましいですね。

Re: 復帰/改行

100000になっても,Fizz[スペースの連続]^H^H^H のようなものを出力すればいいかも。^^;

Re: 復帰/改行

空白で上書きするなら改行("\n")を出力するところでしたほうがよいのでは?

Re: 復帰/改行

なるほど,そうでした。

だれもTeXでは書かな

だれもTeXでは書かないですね(笑)
とりあえず手元には(La)TeX版があります(^^;;

TeX版きぼんぬ

そういえば(La)TeX版はまだ見たことがないですね。ぜひ一番乗りでご発表ください。

これでいいのかな…

(defun gene (n) (if (eq n 1) (list 1)
(append (gene (- n 1))
(if (eq 0 (% n 15)) (list "FizzBuzz")
(if (eq 0 (% n 5)) (list "Buzz")
(if (eq 0 (% n 3)) (list "Fizz")
(list n)))))))
----
2007.06.26 19:15 ソースを見やすく修正。

LaTeX版

それではお言葉に甘えて..
突っ込み歓迎(^^;;
#同じようなコードが
#並んでるのが気に入らないのですが

\documentclass{jarticle}
\begin{document}
%%%Set the upper bound \Upper
\def\Upper{100}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\makeatletter
\def\mod#1#2#3{%
\@tempcntb#2
\divide\@tempcntb#1\relax
\multiply\@tempcntb#1\relax
\advance\@tempcntb-\@tempcnta
\edef#3{\the\@tempcntb}}
\@tempcnta\Upper\relax
\advance\@tempcnta\@ne
\edef\Upper{\the\@tempcnta}
\@tempcnta=\@ne
\loop\ifnum\@tempcnta<\Upper\relax
\mod{3}{\@tempcnta}{\R}%
\ifnum\R=\z@\relax
\mod{5}{\@tempcnta}{\R}%
\ifnum\R=\z@\relax
\the\@tempcnta~FizzBuzz\par
\else
\the\@tempcnta~Fizz\par
\fi
\else
\mod{5}{\@tempcnta}{\R}%
\ifnum\R=\z@\relax
\the\@tempcnta~Buzz\par
\fi
\fi
\advance\@tempcnta\@ne
\par
\repeat
\makeatother
\end{document}

わぁっ

LISP版とLaTeX版,ありがとうございます。

あれれ

たつみさんのLISP版をEmacsで試してみたら (gene 11) で (error "Lisp nesting exceeds `max-lisp-eval-depth'") になっちゃいます。
本田さんのLaTeX版は題意と違うような。

作り直しました

まだ誤読してるかも・・・

\documentclass{jarticle}
\begin{document}
%%%Set the upper bound \Upper
\def\Upper{100}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\makeatletter
\def\check@div#1#2{%
\@tempcnta#1\relax
\@tempcntb#1\relax
\divide\@tempcntb#2\relax
\multiply\@tempcntb#2\relax}
\def\test@FizzBuzz#1{%
\bgroup
\check@div{#1}{3}%
\ifnum\@tempcnta=\@tempcntb
\global\@namedef{#1}{Fizz}
\fi
\check@div{#1}{5}%
\ifnum\@tempcnta=\@tempcntb
\@ifundefined{#1}%
{\global\@namedef{#1}{Buzz}}%
{\global\@namedef{#1}{FizzBuzz}}%
\fi
\@ifundefined{#1}{\global\@namedef{#1}{#1}}{\relax}%
\egroup}
\@tempcnta\Upper\relax
\advance\@tempcnta\@ne
\edef\Upper{\the\@tempcnta}
\@tempcnta\@ne
\loop\ifnum\@tempcnta<\Upper\relax
\test@FizzBuzz{\the\@tempcnta}
\@nameuse{\the\@tempcnta}\par%
\advance\@tempcnta\@ne
\repeat
\makeatletter
\end{document}

?れれ?

うちのemacsではちゃんと動いてますよ…

れれれのれ

はてー,どうしてかな>Lisp版。
完璧のようです>LaTeX版。

なんでかなー

emacsは GNU Emacs 20.7.1 です。動作の様子は、
http://sp.tt.tuat.ac.jp/fizzbuzz-el.gif
に置きました。

Re: なんでかなー

もううちは全部21か22で,Emacs 20が見つかりません。Mac OS X標準の/usr/bin/emacsももう21です。

こんなのもありかと…

箱=ラベル!"ここをみてね!" 作る。
箱! -100 0 位置。
箱!(黄)色。

たけ=タートル!作る。
たけ:歩幅=60 。
たけ:動く=「
 |n|
 たけ ! (3*cos(n*3.6)*(歩幅)) (3*sin(n*3.6)*(歩幅)) 位置。
 「(n % 15)==0」!なら 「箱! 『カメ子カメ太』 書く。」
  そうでなければ「
   「(n % 5)==0」!なら 「箱! 『カメ太』 書く。」
    そうでなければ「
     「(n % 3)==0」!なら 「箱! 『カメ子』 書く。」
      そうでなければ「箱! (n) 書く。」実行。
    」実行。
  」実行。
」。
たけ!ペンなし。
時計=タイマー!作る 0。2秒 間隔 20秒 時間。
時計!「|n| たけ!(n)動く。」実行。

カメ子カメ太

すばらしい!
たけちゃんは歩かないで平行移動するのですね。

回転

たしか「向きを変える」というメッセージがあったと思います。
それを入れてあげればいいだけなんですが、
面倒だったので入れてません。

ほかにもいろいろ考えたのですが、上掲のものを提出します。

Re: 回転

たとえば9行目を
 たけ ! 10 歩く 3.6 左回り。
に変えて,たけ!ペンなし。の後に
たけ!180 歩く。
たけ!90 左回り。
を付け加えたらそれらしくなりませんか。数値は適当ですが。

1行でもかまわないんだ…

はい。そんな1行でできると思います。(単にさぼりました。)

単に

三角関数が出ているのが大げさと思っただけです。
あと,Lispのほうはtail recursionにするか繰返しにするかすれば資源を食わないんでしょうけれど。

たしかに

Dolittleですが、たしかに三角関数なんて使わなくてもできますよね…。参考にした元のソースが、三角関数版だったので、こんなことになりますた。

LISPの方は、ついつい便利な append つかちゃったので、大きくなりましたよね。appendって計算量はおおきいですから…。

Fuzzbuzz問題への対処方法

1:プログラマーにその問題を言い渡したのは何時ですか?
朝?それはまずい。最悪でも夜中の2時にしましょう。
かれらはハイテンションになっているので即座にそのコードを
書き上げるでしょう。

2:5分以内に書けと強制しましたか?
した?それはまずい。かれらはプレッシャーに弱いのです。
ましてや「5分以内にかけないと駄目だよねー」なんて言った日には
ガクブルものです。

3:彼らに糖分は足りていましたか?
たりてない?それはまずい。今すぐコーラを飲ませましょう。
たちどころにコードを仕上げるでしょう。

4:彼らにカレー分は足りていましたか?
たりてない?それはまずい。コードの8割はカレーで出来ています。
今すぐカレーを食べさせましょう。
たちどころにコードどころか仕事を終わらせてくれるでしょう。

5:最速で解けた人へのプレゼントは用意しましたか?
してない。それはまずい。
「5分以内に解けた人にはiPhoneあげるよ」といえば彼らは5分どころか
256ナノセコンドで神でさえ逝っちゃうような素敵なコードを
書いてくれるでしょう。

改良しました!

箱=ラベル!"ここをみてね!" 作る -70 0 位置 (黄) 色。

たけ=タートル!作る ペンなし 0 135 位置 ペンあり (緑) 線の色。
たけ:動く=「 |n|
 たけ ! 10 歩く 3.6 右回り。
 「(n % 15)==0」!なら 「箱! 『カメ子カメ太』 書く。」
  そうでなければ「
   「(n % 5)==0」!なら 「箱! 『カメ太』 書く。」
    そうでなければ「
     「(n % 3)==0」!なら 「箱! 『カメ子』 書く。」
      そうでなければ「箱! (n) 書く。」実行。
    」実行。
  」実行。
」。
時計 = タイマー!作る 0.1秒 間隔 10秒 時間。
時計!「|n| たけ!(n)動く。」実行。

Re: 改良しました!

ゼミで遅れました。どうもありがとうございます。
ドリトルは elsif がないのでネストが深くなっちゃうんですね。DNCLも同じか。

PENによる拡張

PENの開発をしている中村氏も,ネストが深くなるのがいやだということで「そうでなくもし〜ならば」を追加したそうです。
この発言も「スレッドリスト」表示にするとすごくネストが深くなってますが。

ネストが深くなる

Squeak(Smalltalk)もEtoyも同じですね。
コメントのスレッドリスト,初めて試してみました。うーん,ネストが深いときびしいですね。

ドリトルにelsifはあります!

「条件」!なら「…」そうでなければ「条件」なら「…」
そうでなければ「条件」なら「…」実行。

これがマニュアルに載ってないから誰も知らないんだな…
なにしろ構文じゃなく単なるメソッドの使い方だから…

Re: ドリトルにelsifはあります!

ううう,知りませんでした。m(__)m
最初の「条件」だけ「!」なんですね。

Re: ドリトルにelsifはあります!

たけ:動く=「 |n|
 たけ ! 10 歩く 3.6 右回り。
 「(n % 15)==0」!なら 「箱! 『カメ子カメ太』 書く。」
  そうでなければ「(n % 5)==0」なら 「箱! 『カメ太』 書く。」
  そうでなければ「(n % 3)==0」なら 「箱! 『カメ子』 書く。」
  そうでなければ「箱! (n) 書く。」実行。
」。
の「箱!……書く。」が何回も出てきますがこれはfactor outできないんでしょうか。

factor out

動かしてみてませんけどたぶん。
たけ:動く=「 |n|
 たけ ! 10 歩く 3.6 右回り。
 箱!「「(n % 15)==0」!なら 「『カメ子カメ太』」
  そうでなければ「(n % 5)==0」なら 「『カメ太』」
  そうでなければ「(n % 3)==0」なら 「『カメ子』」
  そうでなければ「(n)」実行」書く」。

なるほど…quoteなんですね。

僕も動かしてませんけど…「『カメ子カメ太』」が許されるということは、『』は、エバらないで quote なんですね。

!なら

こうなるとますます最初だけ「!なら」というのが対称性を損なう気がします。シンタックスシュガーで「なら」だけでできるようにするとオブジェクト指向的でなくなるでしょうか。

だめでした

オンライン版のドリトルで実験したら、「n % 1..」が表示されるだけになっちゃいました。
 

よく考えたら駄目に

よく考えたら駄目に決まってるな… すいません私も手続き型
言語の制御構造に毒されているなと思いました。

なお、ドリトルの構文から「!」を全部無くす(あとは今と同じ)、
というのは可能だし検討されたことがあります。

!をなくす

少なくとも「なら」の前は省略可にしたほうが字面の上ではきれいに見えそうですね。ならいっそ全部省略可にするという手も。

動く版です。(すみません。今日は読んでませんでした)

箱=ラベル!"ここをみてね!" 作る -70 0 位置 (黄) 色。

たけ=タートル!作る ペンなし 0 135 位置 ペンあり (緑) 線の色。
たけ:動く=「 |n|
 たけ ! 10 歩く 3.6 右回り。
 箱!(「(n % 15)==0」!なら 「『カメ子カメ太』」
 そうでなければ「(n % 5)==0」なら 「『カメ太』」
 そうでなければ「(n % 3)==0」なら 「『カメ子』」
 そうでなければ「n」実行)書く。
」。
時計 = タイマー!作る 0.1秒 間隔 10秒 時間。
時計!「|n| たけ!(n)動く。」実行。

どもどもm(__)m

カメの神様にわざわざお越しいただいて恐縮です。ありがとうございました。

やっぱりいいのか…

うーん、やっぱり分岐式として使えるよねえ。マニュアルにも
書いた方がいいんじゃないだろうか。少なくともelsifは。

コメントの表示オプション

お好みの表示方法を選択し、「設定の保存」をクリックすると、表示方法を変更することができます。