top / index / prev / next / target / source

2007-07-05 diary: FizzBuzz 問題 - 短くしていく版

いがぴょん画像(小) 日記形式でつづる いがぴょんコラム ウェブページです。

old-v2

FizzBuzz 問題 - 短くしていく版

では、次に FuzzBuzz実装を短くしていく版を作ってみます。

FizzBuzz 問題 - 短くしていく版

では、次に FuzzBuzz実装を短くしていく版を作ってみます。

※注意: ここで挙げている例は ブラックユーモアです。

最初に思いつくのは 文字列連結によるソースコードの短縮についてです。 FizzBuzzShort01.java

/**
 * FizzBuzz: イギリスの学校の子供たちの遊び
 * 
 * 1から100までの数をプリントする。<br>
 * ・3の倍数のときは数の代わりに「Fizz」とプリントする。<br>
 * ・5の倍数のときは数の代わりに「Buzz」とプリントする。<br>
 * ・3と5両方の倍数の場合には数の代わりに「FizzBuzz」とプリントする。
 */
public class FizzBuzzShort01 {
        /**
         * エントリポイント。
         * 
         * @param args
         *            コマンドライン引数。このプログラム内では利用されない。
         */
        public static void main(final String[] args) {
                // TODO:踏み込んだ共通化を行い、ロジックを組み替えました。
                // TODO:マジックナンバーは追放されていません。
                for (int number = 1; number <= 100; number++) {
                        // 3の倍数であるかどうか。
                        final boolean isMultipleOf3 = (number % 3 == 0);
                        // 5の倍数であるかどうか。
                        final boolean isMultipleOf5 = (number % 5 == 0);

                        final StringBuffer bufMsg = new StringBuffer();
                        if (isMultipleOf3) {
                                // 3の倍数のときは「Fizz」を追加する。
                                bufMsg.append("Fizz");
                        }
                        if (isMultipleOf5) {
                                // 5の倍数のときは「Buzz」を追加する。
                                bufMsg.append("Buzz");
                        }
                        if (!isMultipleOf3 && !isMultipleOf5) {
                                // 3の倍数でも5の倍数でも無い時には、数をセットする。
                                bufMsg.append(number);
                        }

                        // 生成された文字列をプリントする。
                        System.out.println(bufMsg.toString());
                }
        }
}

ロジックの組み替えにより 少しずつ短くなっていきます。

3項演算子を利用すると、if 文を省略でき、もっと短くなります。 FizzBuzzShort02.java

/**
 * FizzBuzz: イギリスの学校の子供たちの遊び
 * 
 * 1から100までの数をプリントする。<br>
 * ・3の倍数のときは数の代わりに「Fizz」とプリントする。<br>
 * ・5の倍数のときは数の代わりに「Buzz」とプリントする。<br>
 * ・3と5両方の倍数の場合には数の代わりに「FizzBuzz」とプリントする。
 */
public class FizzBuzzShort02 {
        /**
         * エントリポイント。
         * 
         * @param args
         *            コマンドライン引数。このプログラム内では利用されない。
         */
        public static void main(final String[] args) {
                // TODO:3項演算子を用いて、ロジックを組み替えました。
                // TODO:マジックナンバーは追放されていません。
                for (int number = 1; number <= 100; number++) {
                        // 3の倍数であるかどうか。
                        final boolean isMultipleOf3 = (number % 3 == 0);
                        // 5の倍数であるかどうか。
                        final boolean isMultipleOf5 = (number % 5 == 0);

                        final String strMsg = (isMultipleOf3 ? "Fizz" : "")
                                        + (isMultipleOf5 ? "Buzz" : "")
                                        + (!isMultipleOf3 && !isMultipleOf5 ? String
                                                        .valueOf(number) : "");

                        // 生成された文字列をプリントする。
                        System.out.println(strMsg);
                }
        }
}

ああ、三項演算子は とても素晴らしい!

ここまで来たら、文字列を格納していた変数を無くすことが出来ます。 FizzBuzzShort03.java

/**
 * FizzBuzz: イギリスの学校の子供たちの遊び
 * 
 * 1から100までの数をプリントする。<br>
 * ・3の倍数のときは数の代わりに「Fizz」とプリントする。<br>
 * ・5の倍数のときは数の代わりに「Buzz」とプリントする。<br>
 * ・3と5両方の倍数の場合には数の代わりに「FizzBuzz」とプリントする。
 */
public class FizzBuzzShort03 {
        /**
         * エントリポイント。
         * 
         * @param args
         *            コマンドライン引数。このプログラム内では利用されない。
         */
        public static void main(final String[] args) {
                // TODO:不要な文字列変数を削除しました。また変数名をリファクタリングしました。
                // TODO:マジックナンバーは追放されていません。
                for (int number = 1; number <= 100; number++) {
                        // 3の倍数であるかどうか。
                        final boolean isFizz = (number % 3 == 0);
                        // 5の倍数であるかどうか。
                        final boolean isBuzz = (number % 5 == 0);

                        System.out.println((isFizz ? "Fizz" : "") + (isBuzz ? "Buzz" : "")
                                        + (!isFizz && !isBuzz ? String.valueOf(number) : ""));
                }
        }
}

まだ、いろいろ出来そうですが、ひとまずここで手を止めます。

さて、ここで注意しなくてはならないのは、短くしていく版として記述されたソースコードは FizzBuzz 問題 - リファクタリング版 のソースコードよりも JavaVM上での実行効率が低い可能性があるという点です。Java 5 以降の StringBuilderによる内部展開を期待したとしても オブジェクトのゴミが多く発生する分、FizzBuzz 問題 - リファクタリング版 よりも実行効率が悪くなると想定されるからです…。訓練された Javaプログラマーは ソースコードを見ただけで そこで実行した際のメモリゴミの概算ができるものです。行数の多いFizzBuzz 問題 - リファクタリング版のほうが メモリ効率が良いなんて 不思議で皮肉なものですね…。

一方で、いくら短く記述するためとはいえ、15などの数 (3と5の共通の倍数) を導出したり、あるいは isFizz, isBuzzという変数を省略してしまいたい (省略すると演算の回数が増えてしまう) 衝動にかられるかも知れません。しかし それを省略すると 剰余計算の回数が増え、実行効率は低下するように考えられます。

更に、15などという数値を導出してしまうと、そもそも仕様上には現れない 謎のマジックナンバーがソースコード上に現れることになります。このような数値を導出すると、ソースコードが仕様変更に対して脆弱になってしまう点にも注意が必要です。仕様変更のコントロール権を完全に掌握していないのであれば、なるべく「仕様通り」に実装しておくのが肝心なのです。

関連する日記


この日記について