PHPerKaigi 2024 ⛳️
コードゴルフを解きました。
この記事はQiitaのスライドモードで作ってるよ!
DAY 1以降では僕より短い回答いっぱい出てたので、最短を狙うための記事はもっとほかの人が自分で書いてるのでは!
以後、バイト数は行頭インデントと改行を抜いて計測します。
問題 #1 FizzBuzz
標準回答 (182バイト)
for ($i = 1; $i <= 100; $i++) {
echo match ([$i % 3 === 0, $i % 5 === 0]) {
[true, true] => "FizzBuzz",
[true, false] => "Fizz",
[false, true] => "Buzz",
[false, false] => $i,
}, PHP_EOL;
}
PHP8らしい、美麗なコードですね…
バイト数勝負なので… (126バイト)
for($i=1;$i<=100;$i++) {
echo match([!($i%3),!($i%5)]){
[!0,!0]=>"FizzBuzz",
[!0,!1]=>"Fizz",
[!1,!0]=>"Buzz",
default=>$i,
},"\n";
}
全部バッドノウハウ
- スペースは無駄なので全部詰める
- 「余りの結果は0か0以外か」は
!($i%3)
でboolに -
true
/false
も長すぎるので!0
/!1
に置き換え可能 - PHPの制御構造は中身が単文なら
{}
省略可能
まだいける
ご存じでしたか?
- PHPの配列キーに使えるのはintとstringだけ
- boolで配列アクセスすると
true
/false
に変換される
配列にする (118バイト)
for ($i=1;++$i<101;) {
echo [
0 => [0 => $i, 1 => 'Buzz'],
1 => [0 => 'Fizz', 1 => 'FizzBuzz'],
][!($i%3)][!($i%5)],"\n";
}
配列の0 =>
1 =>
は省略 (87バイト)
for ($i=1;++$i<101;) {
echo [
[$i, 'Buzz'],
['Fizz', 'FizzBuzz']
][!($i%3)][!($i%5)],"\n";
}
ぎゅっと縮めて
for($i=1;$i<=100;$i++)
echo [[$i,'Buzz'],['Fizz','FizzBuzz']][!($i%3)][!($i%5)],"\n";
80バイト
for($i=1;$i++<101;)echo[[$i,'Buzz'],['Fizz','FizzBuzz']][!($i%3)][!($i%5)],"\n";
別解 (86バイト)
echo join("\n", array_map(
fn($i) => ($i%3 ? '' : 'Fizz')
. ($i%5 ? '' : 'Buzz') ?: $i,
range(1,100)
));
別解 (82バイト)
array_map(
fn($i) => print(($i%3 ? '' : 'Fizz')
. ($i%5 ? '' : 'Buzz') ?: $i
)."\n",
range(1,100)
);
問題 #2 Base32
php-playを使って解くときのヒント
wasmではSTDIN
は未定義だが、この方法でユーザー定義できる。
$str = "
aaa
bbb
ccc
";
$f = fopen('php://memory', 'rw');
fwrite($f, $str);
rewind($f);
defined('STDIN') or define('STDIN', $f);
まず考えたこと
長いな…
const TABLE = [
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
'Y', 'Z', '2', '3', '4', '5', '6', '7',
];
こうして…
$tbl = [...range('A','Z'),...range('2','7')];
これを…
foreach (str_split($bits, 5) as $b) {
$base32 .= TABLE[base_convert(str_pad($b, 5, '0'), 2, 10)];
}
こうじゃ
foreach (str_split($bits, 5) as $b) {
$base32 .= [...range('A','Z'),...range('2','7')][
base_convert(str_pad($b, 5, '0'), 2, 10)
];
}
なぜやるのか
一見長くなってそうに見えるが、 const TABLE =
と ;
が削れてる
次に注目するところ
基数の変換 (2進法⇔10進法)
$bits .= str_pad(base_convert(strval(ord($line[$i])), 10, 2), 8, '0', STR_PAD_LEFT);
$base32 .= TABLE[base_convert(str_pad($b, 5, '0'), 2, 10)];
基数変換
専用関数があるので縮まる
$bits .= str_pad(decbin(strval(ord($line[$i]))), 8, '0', STR_PAD_LEFT);
$base32 .= TABLE[bindec(str_pad($b, 5, '0'))];
型を無視する
専用関数があるので縮まる
$bits .= str_pad(decbin(ord($line[$i])), 8, 0, STR_PAD_LEFT);
$base32 .= TABLE[bindec(str_pad($b, 5, 0))];
sprintf()
を使う
$bits .= sprintf('%08d', decbin(ord($line[$i])));
$base32 .= TABLE[bindec(str_pad($b, 5, 0))];
禁断の定数展開
$bits .= str_pad(decbin(ord($line[$i])), 0, 0);
$base32 .= TABLE[bindec(str_pad($b, 5, 0))];
いろいろやって
286バイト
while($l=fgets(STDIN)){$l=chop($l);$t='';for($i=0;$i<strlen($l);$i++)$t.=str_pad(decbin(ord($l[$i])),8,0,0);echo join(array_map(fn($b)=>[...range('A','Z'),...range('2','7')][bindec(str_pad($b,5,'0'))],str_split($t,5))),str_repeat('=',(fn($n)=>($n%8)?0:6-$n/8)(strlen($t)%40))??'',"\n";}
問題 #3 BrainFuck
やはりphp-playで開発しやすいようにこうする
$s = defined('STDIN') ? stream_get_contents(STDIN) : '>+++++++++[<++++++++>-]<.>+++++++[<++++>-]<+.+++++++..+++.[-]>++++++++[<++
++>-]<.>+++++++++++[<+++++>-]<.>++++++++[<+++>-]<.+++.------.--------.[-]>
++++++++[<++++>-]<+.[-]++++++++++.';
これはHello, worldを出力するコード
縮めたらこうなった
$s=fread(STDIN,9999);
$c=$p=0;while($c<strlen($s)){switch($s[$c]){
case'>':$p++;break;
case'<':$p--;break;
case'+':$m[$p]??=0;$m[$p]++;break;
case'-':$m[$p]??=0;$m[$p]--;break;
case'.':echo chr($m[$p]);break;
case'[':if(!($m[$p]??0)){$d=1;while($d>0)match($s[++$c]){'['=>$d++,']'=>$d--,default=>0};}break;
case']':if($m[$p]??0){$d=1;while($d>0)match($s[--$c]){']'=>$d++,'['=>$d--,default=>0};}}$c++;
}
$m[$p]??=0;
の初期化を移動する
$c=$p=0;while($c<strlen($s)){$m[$p]??=0;switch($s[$c]){
case'>':$p++;break;
case'<':$p--;break;
case'+':$m[$p]++;break;
case'-':$m[$p]--;break;
case'.':echo chr($m[$p]);break;
case'[':if(!$m[$p]){$d=1;while($d>0)match($s[++$c]){'['=>$d++,']'=>$d--,default=>0};}break;
case']':if($m[$p]){$d=1;while($d>0)match($s[--$c]){']'=>$d++,'['=>$d--,default=>0};}}$c++;
}