010010111010111011
11101011001110100101101
110100101000101110101
110100101101100110101101
010010111010111011

r423.jp

JavaでFizzBuzzの最短を出すための方法 - 2011/08/31(Wed) 03:38:19
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 {
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やmethodの{}は省略できないけど,条件文,ループ文において条件が満たされた時に実行するコードブロックが1センテンス(;で終わる文)の時,{}は省略できる.forの実行ブロックは8行もあるが,ifで始まる条件文はifブロックを閉じるまでが1センテンスと見なすのでforのブロックも1センテンスとみなす事ができる.なので{}を省略することができる.このコードの空白や改行を無くして340バイト.こっから更にコードを短くしてみようと思う.まず条件文を考える.
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 {
for( int i = 0; i++ < 100; )
System.out.println( ( i % 3 < 1 ? "Fizz" : "" ) + ( i % 5 < 1 ? "Buzz" : i % 3 < 1 ? "" : i ) );
}
}
static initializerのブロックはmain関数が呼ばれる前,クラスをロードする時に呼ばれる.staticなクラスとかのオブジェクトが全てこの時に生成される.main関数はstaticなオブジェクトが全て初期化された後で実行されるため,この様なコードを書くことができるが,その引換として標準エラー出力に
Exception in thread "main" java.lang.NoSuchMethodError: main
というメッセージが出力される.標準出力にはこの様なメッセージはでないので,プログラムの出力としてはオッケーだ.これで102バイトだ.
更に短くしてみる.Javaにはenumという列挙型が定義できる.でもこれはJava VM上でclassとほぼ同じ扱いを受ける.それを使って書いてみる.
enum F {
a;
{
for( int i = 0; i++ < 100; )
System.out.println( ( i % 3 < 1 ? "Fizz" : "" ) + ( i % 5 < 1 ? "Buzz" : i % 3 < 1 ? "" : i ) );
}
}
enumの詳細はJavaの仕様を見てもらうとして,enumにはclassと同様にstatic initializerが定義でき,またinstance initializerという物が定義できる.instance initializerはclassにも定義できるが,instance initializerはインスタンスを作る時にしか実行されない.ちなみにこのinstance initializerはコンストラクタよりも早く実行される.
enumはほぼfinal classの定義とみなすことができ,列挙子は定義したenumの変数として扱われenumのインスタンスが生成,代入される.つまり上述したコードだとenum J (final class Jとほぼ同義)と定義したclassのインスタンスが,aに代入されることとなる.インスタンスが生成されればenumの中に記述したinstance initializerが実行されることとなる.
なのでenumの中に1つだけ列挙子を書き,instance initializerの中にFizzBuzzのコードを書けば,Javaに於ける最短コードが成立する.ちなみに空白改行を削除して97バイト.最短.
comments
Master date : 2013/12/19(Thu) 20:43:41 JST

for文でもwhile文でも96byteで書けるよ.
int i;
というコードでiに何が代入されているか,という点が97byteから96byteへの鍵.

shin date : 2013/11/30(Sat) 16:55:33 JST

96byteのコードですが、for文をwhile文で置き換える事で96Byteのコードを確認できました。

みやっち date : 2013/11/27(Wed) 22:23:37 JST

何度もすみませんm(__)m
どうもキーボードが不調みたいで・・・

やっぱり見当違いでしたか。すみません。
ちなみに96バイトのコードを教えてもらえませんか?

Master date : 2013/11/26(Tue) 16:31:08 JST

3の倍数でFizz,5の倍数でBuzz
3と5の公倍数でFizzBuzzと表示するのが課題なのです!

Master date : 2013/11/26(Tue) 16:29:45 JST

みやっちさん
コメントありがとうございます!でも、そのコードだと3と5の公倍数でFizzしかでなくなりますよ!

みやっち date : 2013/11/25(Mon) 15:58:08 JST

見当違いのところかもしれませんが・・・
System.out.println( ( i % 3 < 1 ? "Fizz" : "" ) + ( i % 5 < 1 ? "Buzz" : i % 3 < 1 ? "" : i ) );

System.out.println( i % 3 < 1 ? "Fizz" : "" + ( i % 5 < 1 ? "Buzz" : i % 3 < 1 ? "" : i ) );
でも動きませんか?

みやっち date : 2013/11/25(Mon) 15:47:44 JST

見当違いのところかもしれませんが・・・
System.out.println( ( i % 3 < 1 ? "Fizz" : "" ) + ( i % 5 < 1 ? "Buzz" : i % 3 < 1 ? "" : i ) );

System.out.println( i % 3 < 1 ? "Fizz" : "" + ( i % 5 < 1 ? "Buzz" : i % 3 < 1 ? "" : i ) );
でも動きませんか?

Master date : 2011/10/05(Wed) 17:30:17 JST

ごめんごめんまだ短くなる余地あった.96バイトが最短.