はじめに
PHPerKaigi(ペチパーカイギ)は、PHPer、つまり、現在PHPを使用している方、過去にPHPを使用していた方、これからPHPを使いたいと思っている方、そしてPHPが大好きな方たちが、技術的なノウハウとPHP愛を共有するためのイベントです。
2024年3月7日(木)〜 3月9日(土)開催でした。
コードゴルフとは
今年はコードゴルフが企画されていました。
与えられたお題を満たすコードをできるだけ短く書け、という競技です。
結果
時間がなくてあまりできなかったですが
問1: 9位
問2: 8位
問3: 6位
という結果でした。
こちらはどういった回答をしたのかを記事にしたいと思います。
解説
問1: FizzBuzz
回答
for($i=0;$i++<100;)echo$i%3?$i%5?$i:"":"Fizz",$i%5?"":"Buzz","\n";
三項演算子を使ってひたすら短く書いたケース。
三項演算子については以下の「例5 三項演算子の短縮形」あたりをご参照ください。
https://www.php.net/manual/ja/language.operators.comparison.php
上位の人たちは未定義変数のwarningをエラー制御演算子でゴリ押すっていうプレイングをしていて目からウロコでした。
余談ながら実はこんなズルをしようとしていました。
https://yamamoto-hiroya.github.io/fizzbuzz.github.io/fizzbuzz.txt
自身のgithub.ioに静的ページ用意してこれを取得して表示するだけ、みたいなやつ。
さすがにズルかなーと思ったのと時間が足りなくてお蔵入りとなりました。
問2: Base32
回答
$a='ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';while($l=rtrim(fgets(STDIN))){$t=$s='';for($i=0;$i<strlen($l);$i++){$t.=str_pad(base_convert(strval(ord($l[$i])),10,2),8,0,0);}foreach(str_split($t,5)as$b){$s.=$a[base_convert(str_pad($b,5,0),2,10)];}$u=strlen($t)%40;echo$s.=($u==32?'=':($u==24?'===':($u==16?'====':($u==8?'======':''))))."\n";}
これだとよく分からないと思うので少し噛み砕きます。
$a='ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
while($l=rtrim(fgets(STDIN))){
$t=$s='';
for($i=0;$i<strlen($l);$i++){
$t.=str_pad(base_convert(strval(ord($l[$i])),10,2),8,0,0);
}
foreach(str_split($t,5)as$b){
$s.=$a[base_convert(str_pad($b,5,0),2,10)];
}
$u=strlen($t)%40;
echo$s.=($u==32?'=':($u==24?'===':($u==16?'====':($u==8?'======':''))))."\n";
}
まず配列で持ってるTABLEを文字列で持ち、それのn番目を参照するように変えることで大幅に削れました。
その後は愚直に変数名短くしたり初期化共通でしたり、stringのところintで渡して暗黙変換させたりと、まぁやりたい放題です。
イコール文字で埋める部分も絶対もっと短くできるのにーと思いながらひとまず三項演算子で誤魔化してました。
上位の人の回答を見るとrangeで初期宣言の配列省略していたり、イコール文字の部分もstr_repeatでうまいことやってたり、すごいです。
問3: Brainf*ck
回答
$s=stream_get_contents(STDIN);$p=$r=0;$m=[];while($p<strlen($s)){$a=$s[$p];$a=='>'?$r++:($a=='<'?$r--:'');if($a=='+')$m[$r]=($m[$r]??0)+1;if($a=='-')$m[$r]=($m[$r]??0)-1;if($a=='.')echo chr($m[$r]);if($a=='['){if(empty($m[$r])||$m[$r]==0){$d=1;while($d>0){$p++;$s[$p]=='['?$d++:($s[$p]==']'?$d--:'');}}}if($a==']'){if(isset($m[$r])&&$m[$r]!==0){$d=1;while($d>0){$p--;$s[$p]==']'?$d++:($s[$p]=='['?$d--:'');}}}$p++;}
こちらも噛み砕いたもの。
$s=stream_get_contents(STDIN);$p=$r=0;$m=[];
while($p<strlen($s)){
$a=$s[$p];
$a=='>'?$r++:($a=='<'?$r--:'');
if($a=='+')$m[$r]=($m[$r]??0)+1;
if($a=='-')$m[$r]=($m[$r]??0)-1;
if($a=='.')echo chr($m[$r]);
if($a=='['){
if(empty($m[$r])||$m[$r]==0){
$d=1;
while($d>0){
$p++;
$s[$p]=='['?$d++:($s[$p]==']'?$d--:'');
}
}
}
if($a==']'){
if(isset($m[$r])&&$m[$r]!==0){
$d=1;
while($d>0){
$p--;
$s[$p]==']'?$d++:($s[$p]=='['?$d--:'');
}
}
}
$p++;
}
基本的に全て曖昧比較にしてイコールの数を減らす。
Null合体演算子でif文省略。
switch文はbreakが必要になるなーと思ったのでif文に置き換え。
elseifは文字量が長いので全て同列のif文。
!isset
よりemtpy
が短かったのでそちらに。
あとはいつもどおり変数名短くして三項演算子にしてーみたいな感じ。
正直3問目は全然練られてなく、共通にできそうな部分が多々あり、まだやれたなー感が残る終わりとなりました。
上位の方の回答を見ましたが何がなにやら…wという感じでした。
共通にできる部分を関数にしてることだけは分かった。
まとめ
コードゴルフ全般に言えますが業務でこんなコード書いたら可読性ゼロなので絶対に真似しないでくださいw
あくまで実装のテクニックのひとつ・娯楽や競技の類としてご参照ください。