メンバーのランレングスCarelyちゃんを短かくする | Dev Driven 開発・デザインチーム メンバーのランレングスCarelyちゃんを短かくする | 働くひとと組織の健康を創る iCARE

BLOG

メンバーのランレングスCarelyちゃんを短かくする

2022/12/19

おぎじゅんです。VPoEのくどうさんが3日目に出したお題のランレングスCarelyちゃん、それなりに盛り上がっていてうれしいです。
今日はメンバーのhiroendoreが書いたコードを思想はそのままに短かくしてみようと思います。

print "0v,12,n0u,0m,1k,n0l,0i,1s,n0h,0f,1y,n0e,0d,1z,13,n0c,0b,1z,17,n0a,09,1e,0i,1e,n08,07,1d,0o,1c,n07,06,1c,0s,1c,n05,05,1b,0w,1b,n04,04,1a,0z,01,1a,n03,03,1a,09,13,0e,13,0a,19,n02,03,19,0a,14,0c,14,n0l,02,19,0c,11,0g,11,n0m,01,19,0z,n0i,01,19,0z,n0i,01,19,0z,n0i,01,18,0d,12,0g,11,n0m,01,18,0f,13,0a,12,n0o,01,18,0j,17,n0s,01,18,0z,n0j,01,19,0z,02,11,n0f,01,19,0v,1e,n08,02,19,0r,1j,n06,02,19,0p,1n,n04,03,19,0n,1p,n03,03,1a,0l,1r,n02,04,1a,0k,1s,n01,05,1b,0i,1s,n01,06,1c,0g,1s,n01,06,1e,0e,1r,n02,06,1i,0b,1q,n02,05,1r,04,1n,n04,05,1s,05,1l,n04,04,1a,01,1k,05,1k,n03,03,16,09,1i,0a,14,04,17,n02,03,12,0i,1g,0k,12,n02,0z,n0s".split(',').map{ |e| e[0]=='n'? e[1]e[2].to_i(36)+"\n": e[0]e[1].to_i(36)}.join

こんな感じでした。36進数に変換するあたりは私が4日目で書いたコードも同じなのですが、改行のnをどう処理するかで56byteの差がついたようですね。
せっかくなので、そのままだと悔しいのでこのコードの仕組みはそのままにもう少し短かくしてみることにします。
えいっ

print %w(0v 12 n0u 0m 1k n0l 0i 1s n0h 0f 1y n0e 0d 1z 13 n0c 0b 1z 17 n0a 09 1e 0i 1e n08 07 1d 0o 1c n07 06 1c 0s 1c n05 05 1b 0w 1b n04 04 1a 0z 01 1a n03 03 1a 09 13 0e 13 0a 19 n02 03 19 0a 14 0c 14 n0l 02 19 0c 11 0g 11 n0m 01 19 0z n0i 01 19 0z n0i 01 19 0z n0i 01 18 0d 12 0g 11 n0m 01 18 0f 13 0a 12 n0o 01 18 0j 17 n0s 01 18 0z n0j 01 19 0z 02 11 n0f 01 19 0v 1e n08 02 19 0r 1j n06 02 19 0p 1n n04 03 19 0n 1p n03 03 1a 0l 1r n02 04 1a 0k 1s n01 05 1b 0i 1s n01 06 1c 0g 1s n01 06 1e 0e 1r n02 06 1i 0b 1q n02 05 1r 04 1n n04 05 1s 05 1l n04 04 1a 01 1k 05 1k n03 03 16 09 1i 0a 14 04 17 n02 03 12 0i 1g 0k 12 n02 0z n0s).map{_1[0]=='n'? _1[1]_1[2].to_i(36)+"\n": _1[0]_1[1].to_i(36)}.join

できたコードがこちらです。
Rubyには、%記法というものがあり、文字列や数値、配列などを効率的に記述することができます。例えば

%w(foo bar baz)

と書くと

['foo', 'bar', 'baz']

と書いたのと同じことになります。これを利用して直接配列を記述することにより、split(',')の分の文字数を節約しています。
また、mapの中で使っているブロック変数をデフォルトブロック変数にすることにより|e|の定義を省略しました。ただこれは変数が2文字になってしまうので合計で1byteしか節約になっていませんね..。
以上で702byteです。うーん、700切りたいですねえ。微調整してみましょう。

puts %w(0v 12 n0u 0m 1k n0l 0i 1s n0h 0f 1y n0e 0d 1z 13 n0c 0b 1z 17 n0a 09 1e 0i 1e n08 07 1d 0o 1c n07 06 1c 0s 1c n05 05 1b 0w 1b n04 04 1a 0z 01 1a n03 03 1a 09 13 0e 13 0a 19 n02 03 19 0a 14 0c 14 n0l 02 19 0c 11 0g 11 n0m 01 19 0z n0i 01 19 0z n0i 01 19 0z n0i 01 18 0d 12 0g 11 n0m 01 18 0f 13 0a 12 n0o 01 18 0j 17 n0s 01 18 0z n0j 01 19 0z 02 11 n0f 01 19 0v 1e n08 02 19 0r 1j n06 02 19 0p 1n n04 03 19 0n 1p n03 03 1a 0l 1r n02 04 1a 0k 1s n01 05 1b 0i 1s n01 06 1c 0g 1s n01 06 1e 0e 1r n02 06 1i 0b 1q n02 05 1r 04 1n n04 05 1s 05 1l n04 04 1a 01 1k 05 1k n03 03 16 09 1i 0a 14 04 17 n02 03 12 0i 1g 0k 12 n02 0z 0s).map{_1[0]=='n'? _1[1]_1[2].to_i(36)+"\n": _1[0]_1[1].to_i(36)}.join

printputsに変えて、さらに最後の行の改行は不要なので1文字削りましたが、ちょうど700byteです。惜しい..。
もう少し見てみましょう。元のコードでe[1]*e[2].to_i(36)e[0]*e[1].to_i(36)が繰り返されているのが気になります。要するに改行の処理をしているところなんですが、これnを先頭じゃなくて末尾に持ってきたらどうでしょう。
できたコードはこんな感じです。

puts %w(0v 12 0un 0m 1k 0ln 0i 1s 0hn 0f 1y 0en 0d 1z 13 0cn 0b 1z 17 0an 09 1e 0i 1e 08n 07 1d 0o 1c 07n 06 1c 0s 1c 05n 05 1b 0w 1b 04n 04 1a 0z 01 1a 03n 03 1a 09 13 0e 13 0a 19 02n 03 19 0a 14 0c 14 0ln 02 19 0c 11 0g 11 0mn 01 19 0z 0in 01 19 0z 0in 01 19 0z 0in 01 18 0d 12 0g 11 0mn 01 18 0f 13 0a 12 0on 01 18 0j 17 0sn 01 18 0z 0jn 01 19 0z 02 11 0fn 01 19 0v 1e 08n 02 19 0r 1j 06n 02 19 0p 1n 04n 03 19 0n 1p 03n 03 1a 0l 1r 02n 04 1a 0k 1s 01n 05 1b 0i 1s 01n 06 1c 0g 1s 01n 06 1e 0e 1r 02n 06 1i 0b 1q 02n 05 1r 04 1n 04n 05 1s 05 1l 04n 04 1a 01 1k 05 1k 03n 03 16 09 1i 0a 14 04 17 02n 03 12 0i 1g 0k 12 02n 0z 0s).map{_1[0]*_1[1].to_i(36)+(_1[2]=='n'? "\n":'')}.join

これで一気に685byteまで縮まりました。
というわけで、いくつかのRubyの書き方を追及することにより700byteを切ることが出来ました。次なる挑戦者を求む!