GOTO M.

趣味のコーディングとか、勉強とか、読書とか

Code golf 反省メモ(デスマコロシアム12)(Falcon)

挑戦した問題

codeiq.jp

要約

解説記事から引用します。

はじめは、aからzの文字でcodeiqに一致する文字のみを大文字に変換します。 次にaからzの文字でdpefjr(codeiqの次の文字)に一致する文字のみを大文字に変換します。 という風に、ループする度に大文字に置換する位置をずらす処理をaからzに対して22回繰り返します。

ソースコード

自分のFalcon(57)
//0.upto(571,{i=>print(chr(97-32*(82204>>(i%26-int(i/26))&&1)+i%26))})
//times(572,{i=>print(chr(97-32*(82204>>(i%26-int(i/26))&&1)+i%26))})
//times(572,{i=>>>""%(97-32*(82204>>(i%26-int(i/26))&&1)+i%26)})
//times(22,{i=>times(26,{j=>>>(c='A'/j)/-i in"CODEIQ"?c:c/32})})
  times(572,{i=>>>(c='A'/(i%26))/-(i/26)in"CODEIQ"?c:c/32})
  • char変数についてASCIIコードの加減算に使える"/"演算子(int->string)が独特でした。(といいつつi/26は小数なので、引数はintじゃなくてもいいのかな)
  • 下記のangel様の使用されている"/="のリテラルへの適用なども試していましたが、一重ループのこの形ではコード短縮に至りませんでした。
1位の方(angel様)のFalcon(53)の吟味
  for i=7 to 28:for j=0 to 25:>>"a"/=j-=32&&20551<<i>>j
  • デスマコロシアム(第12回・最終回?)に参加しました - ange1のブログ より。
  • 二重ループを使っている点、"CODEIQ"の包含判定に「in 文字列」でなく「十進bit列のシフト演算」を使っている点が私のコードと異なっています。
  • 他に気付くのは、ループにtimes関数でなく通常のfor文を使われている点です。確かに下記のように、ループ部分を抜き出すとangel様の方法の方が短くなっています(回す範囲は微妙に違いますが。)Rubyに慣れてきたことで先入観を持ってしまっていました。
times(22,{i=>times(26,{j=>doSomething})})
for i=7 to 28:for j=0 to 25:doSomething
  • 更に、-=32&&20551<<i>>jの部分も目から鱗の処理でした。下記①②だけ考えており、③は思い付けませんでした。演算子の優先順位を意識しつつ括弧を減らすのに便利そうなテクニックです。
j + 32*【0/1となる計算処理】                    //①
j + 【t/fとなる計算処理】?0:32                  //②
j + 32&&【右から6ビット目が0/1となる計算処理】  //③(※&&はビットAND)
  • また細かい点ですが、以下の1バイト差も大きいです。
n<<i>>j
n<<(i-j)

今後の Code golf に向けた反省

  • timesループが常にforループより安価だとは限らない。慣れない言語に触る時は一度両方試みること。(その他の構文にも言える)
  • 演算子の優先順位のせいで余計なカッコが付くときは、angel様コード吟味の4,5点目のように他演算子による代替案を考えること。
  • 二重ループ+シフト演算というパターンが検討されていなかった。複数の選択肢の組み合わせについて、もっと網羅的に試すようにすること。
  • 精神論。「1位は自分の知らない命令を見つけているのでは」という、ある意味楽観的な観測にすがりがち。それでgithubのテストケース全部読むことに時間を費やすよりは、もう少し頭を使う方向で縮める努力をすること。