keyword: computing
FizzBuzzっつうポピュラーなプログラミング練習の課題があります.
元は言葉遊びらしく元の遊びを知らないけど,3の倍数の時にアホになるとかそんな感じです.んでCodeGolfっつう最短コードを書いた人が最もえらいプログラミング競技というかゲームがあります.
ここでFizzBuzzの課題をあえてJavaで取り組んでみたいと思う.
そもそもJavaは前提としてclassを定義しないとプログラムは書けないそんな増長なプログラミング言語です.そんな言語で最短を目指すからこの競技が面白いんであって,もともと短く書ける言語で取り組んでも面白く無いだろうが!とか言う偏屈じじいの小理屈は置いておいて早速レギュラーなコードを書いてみようと思う.
課題の詳細はこっちを見て欲しい.http://golf.shinh.org/p.rb?FizzBuzz
public class F {まあこんな感じだと思う.
public static void main(String[] a) {
for (int i = 1; i <= 100; i++) {
if ( i % 3 == 0 && i % 5 == 0 ) {
System.out.println("FizzBuzz");
} else if ( i % 3 == 0) {
System.out.println("Fizz");
} else if ( i % 5 == 0) {
System.out.println("Buzz");
} else {
System.out.println(i);
}
}
}
}
さてじゃあ次にここから省略できるものを削ぎ落してみよう.
class F {classやmethodの{}は省略できないけど,条件文,ループ文において条件が満たされた時に実行するコードブロックが1センテンス(;で終わる文)の時,{}は省略できる.forの実行ブロックは8行もあるが,ifで始まる条件文はifブロックを閉じるまでが1センテンスと見なすのでforのブロックも1センテンスとみなす事ができる.なので{}を省略することができる.このコードの空白や改行を無くして340バイト.こっから更にコードを短くしてみようと思う.まず条件文を考える.
public static void main(String[] a) {
for (int i = 1; i <= 100; i++)
if ( i % 3 == 0 && i % 5 == 0 )
System.out.println("FizzBuzz");
else if ( i % 3 == 0)
System.out.println("Fizz");
else if ( i % 5 == 0)
System.out.println("Buzz");
else
System.out.println(i);
}
}
class F {こんな感じでどうだろう.
public static void main(String[] a) {
for( int i = 0; i++ < 100; )
System.out.println( ( i % 3 < 1 ? "Fizz" : "" ) + ( i % 5 < 1 ? "Buzz" : i % 3 < 1 ? "" : i ) );
}
}
if文は三項演算子に置き換え,forの実行ブロックを実行後に行う式は無くしてみた.替りに評価式にi++を入れてみた.
インクリメント演算子が変数の後ろにある場合,式が終わった後で値がインクリメントされる.つまりここのfor文の評価式はiを評価した後でiが加算され,i = 0の時は0で評価し実行ブロックではiが1となる.これで131バイトとだいぶ短くなった.
たぶんレギュラーで省略できることはこれでないと思う.言い換えると(おそらく)どの言語でも使えるテクニックはここまで.こっからはJavaの実行時の挙動を知らなければ短くできない.
まず試しにstatic initializerを使ってmainメソッドを無くしてみよう.
class F {static initializerのブロックはmain関数が呼ばれる前,クラスをロードする時に呼ばれる.staticなクラスとかのオブジェクトが全てこの時に生成される.main関数はstaticなオブジェクトが全て初期化された後で実行されるため,この様なコードを書くことができるが,その引換として標準エラー出力に
static {
for( int i = 0; i++ < 100; )
System.out.println( ( i % 3 < 1 ? "Fizz" : "" ) + ( i % 5 < 1 ? "Buzz" : i % 3 < 1 ? "" : i ) );
}
}
Exception in thread "main" java.lang.NoSuchMethodError: mainというメッセージが出力される.標準出力にはこの様なメッセージはでないので,プログラムの出力としてはオッケーだ.これで102バイトだ.
更に短くしてみる.Javaにはenumという列挙型が定義できる.でもこれはJava VM上でclassとほぼ同じ扱いを受ける.それを使って書いてみる.
enum F {enumの詳細はJavaの仕様を見てもらうとして,enumにはclassと同様にstatic initializerが定義でき,またinstance initializerという物が定義できる.instance initializerはclassにも定義できるが,instance initializerはインスタンスを作る時にしか実行されない.ちなみにこのinstance initializerはコンストラクタよりも早く実行される.
a;
{
for( int i = 0; i++ < 100; )
System.out.println( ( i % 3 < 1 ? "Fizz" : "" ) + ( i % 5 < 1 ? "Buzz" : i % 3 < 1 ? "" : i ) );
}
}
enumはほぼfinal classの定義とみなすことができ,列挙子は定義したenumの変数として扱われenumのインスタンスが生成,代入される.つまり上述したコードだとenum J (final class Jとほぼ同義)と定義したclassのインスタンスが,aに代入されることとなる.インスタンスが生成されればenumの中に記述したinstance initializerが実行されることとなる.
なのでenumの中に1つだけ列挙子を書き,instance initializerの中にFizzBuzzのコードを書けば,Javaに於ける最短コードが成立する.ちなみに空白改行を削除して97バイト.最短.