2020年04月14日
なんとなくJuliaのFizzBuzzでのコードゴルフをやってみました。
たまたまこのページ( 最短であってほしい Julia の FizzBuzz )を見つけたのでやってみようかと思いました。
この記事で最後に到達したのがこのコード
(i->println((x="Fizz"^(i%3÷2)*"Buzz"^(i%5÷4))>"" ? x : i+1)).(0:99)
67文字ですが2バイトの"÷"を使っているため69bytesです。
これを元になんとか縮めていきます。
まずこの"÷"の2バイトを1バイトの文字でどうにかできないか考えます。
ここでの(i%3÷2)
というのはiを3で割ったときのあまりが2のときだけ1でほかは0というのを表しています。
2値ということはtrue,falseで代用できそうです。
幸いJuliaのtrueとfalseはIntegerのサブタイプで実質的に1と0のみ取る整数という扱いができます。
というわけでjuliaのREPLでテストしてみます
julia> "Fizz"^(true)
"Fizz"
julia> "Fizz"^(false)
""
となり(1,0)じゃなく、(true,false)でも同様の結果が得られることがわかります。
というわけで(i%3÷2)
は(i%3>1)
と書き換えることができます。
これで67bytes(67文字)です。
(i->println((f="Fizz"^(i%3>1)*"Buzz"^(i%5>3))>"" ? x : i+1)).(0:99)
次になんかスペースが多いので減らせないかなと考えました。
というわけで三項演算子を使わず配列とインデックスで2つの分岐を行う作戦にしてみます。
そこでできたのがこれ
(i->println([i+1,"Fizz"^(a=i%3>1)*"Buzz"^(b=i%5<1)][a|b+1]).(0:99)
これは実は短くなっていなくて同じ文字数です。
残念でした。
インデックスのためのa,bを定義したりカッコが多くなってしまい短くはなりませんでした。
うーん、残念。
このコードを見て次の作戦を考えます。
i+1
の+1
って長いな。
というわけでインデックスをずらして(意図的にずらされていたのをもとに戻して)やってみます
(i->println([i,"Fizz"^(a=i%3<1)*"Buzz"^(b=i%5<1)][a|b+a])).(1:100)
これで1文字減って66bytesです。
やったぜ。
実は1:100のインデックスのやつ最初の三項演算子のバージョンでも同様に使えて1文字短くできます。
次に考えたのは「aとbの定義で結構長くなってるので省略できない?」です。
i
と"Fizz"^(i%3<1)*...
を比べてどちらかをいい感じに選んでくれれば良さそうです。
で考えたのはこれ
(i->println(max(i,"Fizz"^(i%3<1)*"Buzz"^(i%5<1)))).(1:100)
かなり短くなりましたが実はこれは動きません。
よく考えるとIntとStringをmax関数に突っ込んでます。
JavaScriptやPHPあたりならどっちか出してくれそうな雰囲気ですが…
まあこれはできないとしてこの路線を試します。
iをStringにしてやればmax関数が使えるのでこちらを使います。
(i->println(max(string(i),"Fizz"^(i%3<1)*"Buzz"^(i%5<1)))).(1:100)
これはちゃんと動くぞ!
ただし同じ66bytes…
なぜかコードゴルフをやってるとこういういい感じにブレイクスルーしたつもりが文字数が縮まってないことがよく起こります。
ここでstring()が長すぎるぞということで別のを探します。
いいのがありました。
(i->println(max(repr(i),"Fizz"^(i%3<1)*"Buzz"^(i%5<1)))).(1:100)
これで2文字縮まって64bytes
結構縮まりました。
ここで終わりかと思ったんですがこんなことしなくてももっと簡単に数を文字列に変更する方法がありました。
というわけでこれ。
(i->println(max("$i","Fizz"^(i%3<1)*"Buzz"^(i%5<1)))).(1:100)
というわけで61bytesになりました。
(追記)
ここまでできたのですがなんかもう少し調べてみたところ61bytesの物を見つけました
1バイト削るのに2日かかった…61bytes
— あんちもん2 (@antimon2) March 1, 2019
julia -e '1:100 .|>i->println((:FizzBuzz,:Buzz,:Fizz,i)[2i^4%5-~i^2%3])'
(てかまだやってたw)https://t.co/mmLqdvYqOY
パイプライン演算子を使うのは自分も考えたのですがなんか短くならなかった(というか長くなった)ので見送っていたのですがこうすると関数定義のカッコを減らすことができるため1文字短くできるようです
というわけでこれを取り入れると最終的に次のコードになります
60bytes
1:100 .|>i->println(max("$i","Fizz"^(i%3<1)*"Buzz"^(i%5<1)))