LoginSignup
10
7

PHPerKaigi 2024 コードゴルフを解く (DAY 0)

Last updated at Posted at 2024-03-10
1 / 39

PHPerKaigi 2024 ⛳️

コードゴルフを解きました。


この記事はQiitaのスライドモードで作ってるよ!
DAY 1以降では僕より短い回答いっぱい出てたので、最短を狙うための記事はもっとほかの人が自分で書いてるのでは!


以後、バイト数は行頭インデントと改行を抜いて計測します。


問題 #1 FizzBuzz


スクリーンショット 2024-03-07 1.35.50.png


標準回答 (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


スクリーンショット 2024-03-07 2.59.11.png


スクリーンショット 2024-03-07 3.00.42.png


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++;
}
10
7
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
7