-- / --
--
上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

12 / 17
Tue

最近寒くなったと感じる Ziphil です。

Kotlin は Java に比べてコードが短くなるという話なので、 Kotlin 初心者ながらも、 コードゴルフに挑戦してみました。 テーマは 1 から 100 までの FizzBuzz です。 なお、 FizzBuzz の結果は標準出力に表示するものとして、 fun main(args: Array<String>) とかを除いた、 処理部分だけを短くします。 それと、 標準ライブラリ以外はインポートしないものとします。

さて、 まずは何も気にせず普通にコードを書いてみましょうー!

for (i in 1..100) {
  if (i % 15 == 0) {
    println("FizzBuzz")
  } else if (i % 3 == 0) {
    println("Fizz")
  } else if (i % 5 == 0) {
    println("Buzz")
  } else {
    println(i)
  }
}

200 Byte です。 これをこれから短くしていきます! まず、 for 文は in やその前後のスペースとかで文字数を稼いでしまっているので、 代わりに forEach を使ってみます。 ついでに、 不要なスペースやカッコを除去します。

(1..100).forEach{if(it%15==0)println("FizzBuzz")else if(it%3==0)println("Fizz")else if(it%5==0)println("Buzz")else println(it)}

127 Byte です。 さて、 コードゴルフでは、 同じものが繰り返されているは良くないと言われていますね。 コードを見てみると、 else if とか else が何個もあって文字数を稼いでいるので、 これを何とかして消したいものです。 そこで、 when 文を使ってみます。 Kotlin の when 文は、 単に if ~ else if の連続を見やすく書き直すことにも使われます。

(1..100).forEach{when{it%15==0->println("FizzBuzz");it%3==0->println("Fizz");it%5==0->println("Buzz");else->println(it)}}

121 Byte です。 さて、 println もたくさんありますから、 これも 1 つにまとめちゃいましょう!

(1..100).forEach{println(when{it%15==0->"FizzBuzz";it%3==0->"Fizz";it%5==0->"Buzz";else->it})}

93 Byte です。 100 Byte を切りましたが、 ここからが難しいんです・・・。 まず、 剰余算の返り値は必ず 0 以上の整数なので、 == 0< 1 に置き換えて、 悪あがきします。

(1..100).forEach{println(when{it%15<1->"FizzBuzz";it%3<1->"Fizz";it%5<1->"Buzz";else->it})}

91 Byte になりました。 うー・・・。 限界ですかねぇ・・・。 FizzBuzz が 2 回出てくるので、 これをそれぞれ 1 回ずつにしたいところですが・・・。 そこで、 Fizz と Buzz の表示判定をそれぞれ別々に行ってみましょう。

(1..100).forEach{println(when{it%3<1->"Fizz";else->""}+when{it%5<1->"Buzz";else->""})}

これだと 3 の倍数でも 5 の倍数でもない場合に数字が表示できませんね・・・。 だったら、 最後に数字をつけ足せばいいじゃない。

(1..100).forEach{println(when{it%3<1->"Fizz";else->""}+when{it%5<1->"Buzz";else->""}+"${it}")}

・・・いやいや、 これだと Fizz とかを表示する場合も数字が表示されちゃいます。 だったら、 Fizz とか Buzz が表示される場合は数字を消せば良いじゃない。

(1..100).forEach{println((when{it%3<1->"Fizz";else->""}+when{it%5<1->"Buzz";else->""}+"${it}").replaceAll("z\\d+","z"))}

120 Byte です。 いや、 増えてるじゃないですか!! ・・・んー、 ということで、 何か使える関数がないか、 Kotlin の API を眺めてみると・・・、 drop ってのがありました! これを使うと、 指定した文字数分だけ最初から取り除いた文字列が得られます。 これで when 文が消せるんじゃないか・・・?

とりあえず、 Fizz の表示判定を考えてみます。 it が 3 の倍数のとき 0 になり、 それ以外なら 4 になるような計算式があれば、 それを drop の引数にすれば、 うまくいきそうです。 できるのか・・・?

考えた結果、 導き出された式は it % 3 * 4 です。 3 の倍数でないとき 4 じゃなきゃいけないわけじゃないんです。 要は 「Fizz」 って文字列を全部消したいだけですから、 4 以上なら何でも良いわけです。 ということで、 書き直します。

(1..100).forEach{println(("Fizz".drop(it%3*4)+"Buzz".drop(it%5*4)+"${it}").replaceAll("z\\d+","z"))}

100 Byte です。 うわ、 短くなってないですねー・・・。 "Fizz".drop(it%3*4) という発想は良かったかもしれませんが、 やっぱり replaceAll 関数が文字数取ってますね・・・。

結局、 私には 91 Byte が限界でした。 最後の drop の発想は良かったと思うんですけどねー。 substring も使えそうなんですが、 そもそも関数名が長いから乱用できませんし。

コードゴルフ、 考えるのは楽しいですけど難しいですね。 80 Byte くらいにはなりそうですが、 良い方法が思いつきません。 皆さんも考えてみてはどうでしょうか?


12/17・・・が、 Twitter でコードゴルフをやってみたということを呟いたら、 @ngsw_taro さんからさらに短くなったコードをいただきました。

(1..100)map{a->println(when{a%15<1->"FizzBuzz";a%5<1->"Buzz";a%3<1->"Fizz";else->a})}

85 Byte です。 forEach にする必要がなかったというのは盲点でしたね。


12/27・・・これが最短かなーとか思っていたら、 @ayato_p さんから指摘が。

1..100map{a->println(when{a%15<1->"FizzBuzz";a%5<1->"Buzz";a%3<1->"Fizz";else->a})}

83 Byte になりました。 カッコいらないんですねー・・・。

スポンサーサイト

comment ×0
コメント
管理者にだけ表示を許可する
 
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。