From 4f46d262e6967c9c638b40f3b0246d21b7a9b9dc Mon Sep 17 00:00:00 2001 From: nsfisis Date: Wed, 9 Apr 2025 20:29:15 +0900 Subject: feat(blog/nuldoc): rebuild --- .../2022-04-09/phperkaigi-2022-tokens/index.html | 216 +++++++-------------- 1 file changed, 70 insertions(+), 146 deletions(-) (limited to 'vhosts/blog/public/posts/2022-04-09') diff --git a/vhosts/blog/public/posts/2022-04-09/phperkaigi-2022-tokens/index.html b/vhosts/blog/public/posts/2022-04-09/phperkaigi-2022-tokens/index.html index 3469e8b1..ca22ff5a 100644 --- a/vhosts/blog/public/posts/2022-04-09/phperkaigi-2022-tokens/index.html +++ b/vhosts/blog/public/posts/2022-04-09/phperkaigi-2022-tokens/index.html @@ -67,22 +67,19 @@
-

はじめに

+

はじめに

- 本日開始された PHPerKaigi 2022 の PHPer チャレンジにおいて、弊社 デジタルサーカス株式会社 の問題を 3問作成した。この記事では、これらの問題の解説をおこなう。 + 本日開始された PHPerKaigi 2022 の PHPer チャレンジにおいて、弊社 デジタルサーカス株式会社 の問題を 3問作成した。この記事では、これらの問題の解説をおこなう。

-

- リポジトリはこちら: https://github.com/nsfisis/PHPerKaigi2022-tokens + リポジトリはこちら: https://github.com/nsfisis/PHPerKaigi2022-tokens

-
-

第1問 brainf_ck.php

+

第1問 brainf_ck.php

- ソースコードはこちら。実行には PHP 8.1 以上が必要なので注意。 + ソースコードはこちら。実行には PHP 8.1 以上が必要なので注意。

-
<?php
 
@@ -154,34 +151,28 @@
   $👈, $📝,
 ]);
-

- この問題は、単に適切なバージョンの PHP で動かせばトークンが得られる。 + この問題は、単に適切なバージョンの PHP で動かせばトークンが得られる。

-
-

解説

+

解説

-

絵文字

+

絵文字

- まず目につくのは大量の絵文字だろう。 PHP は識別子に使用できる文字の範囲が広く、絵文字も使うことができる。 + まず目につくのは大量の絵文字だろう。 PHP は識別子に使用できる文字の範囲が広く、絵文字も使うことができる。

-
-

プログラム全体

+

プログラム全体

- Brainf*ck のインタプリタとプログラムになっている。 Brainf*ck とは、難解プログラミング言語のひとつであり、ここで説明するよりも Wikipedia の該当ページを読んだ方がよい。 + Brainf*ck のインタプリタとプログラムになっている。 Brainf*ck とは、難解プログラミング言語のひとつであり、ここで説明するよりも Wikipedia の該当ページを読んだ方がよい。

-

- https://ja.wikipedia.org/wiki/Brainfuck + https://ja.wikipedia.org/wiki/Brainfuck

-

- なお、brainf*ck プログラムを普通の書き方で書くと、次のようになる。 + なお、brainf*ck プログラムを普通の書き方で書くと、次のようになる。

-
+ + + + + + + + + +
 [
@@ -206,89 +197,71 @@
 > - .
 < .
-

- 実行結果はこちら: https://ideone.com/22VWmb + 実行結果はこちら: https://ideone.com/22VWmb

-

- それぞれの絵文字で表された関数が、各命令に対応している。 + それぞれの絵文字で表された関数が、各命令に対応している。

-
  • $👉: >
  • -
  • $👈: <
  • -
  • $👍: +
  • -
  • $👎: -
  • -
  • $📝: .
  • -
  • $🤡: [
  • -
  • $🎪: ]
-

, (入力) に対応する関数はない (このプログラムでは使わないので用意していない)。

-

- なお、$🐘 はいわゆる main 関数であり、プログラムの実行部分である。 + なお、$🐘 はいわゆる main 関数であり、プログラムの実行部分である。

-
-

絵文字の選択

+

絵文字の選択

- おおよそ意味に合致するよう選んでいるが、$🤡$🎪 は弊社デジタルサーカスにちなんでいる。 また、$🐘 は PHP のマスコットの象に由来する。 + おおよそ意味に合致するよう選んでいるが、$🤡$🎪 は弊社デジタルサーカスにちなんでいる。 また、$🐘 は PHP のマスコットの象に由来する。

-
-

strict_types

+

strict_types

declare 文の strict_types に指定できるのは、01 の数値リテラルだが、 0x00b1 のような値も受け付ける。 今回は、PHP 8.1 から追加された、0O または 0o から始まる八進数リテラルを使った。

-
-

URL

+

URL

- ソースコードのライセンスを示したこの部分だが、 + ソースコードのライセンスを示したこの部分だが、

-
https://creativecommons.org/publicdomain/zero/1.0/
-

- 完全に合法な PHP のコードである。 https: 部分はラベル、// 以降は行コメントになっている。 + 完全に合法な PHP のコードである。 https: 部分はラベル、// 以降は行コメントになっている。

-
-

リテラルなしで数値を生成する

+

リテラルなしで数値を生成する

- ソースコード中に、ほとんど数値リテラルが書かれていないことにお気づきだろうか。PHP では、型変換を利用することで任意の整数を作り出すことができる。 + ソースコード中に、ほとんど数値リテラルが書かれていないことにお気づきだろうか。 PHP では、型変換を利用することで任意の整数を作り出すことができる。

-
assert(0 === +!![]);
 assert(1 === +![]);
@@ -296,46 +269,38 @@
 assert(3 === ![]+![]+![]);
 assert(10 === +(![].+!![]));
-

[]! を適用すると true が返ってくる。それに + を適用すると、bool から int ヘの型変換が走り、1 が生成される。10 はさらにトリッキーだ。まず 10 を作り、. で文字列として結合する ('10')。これに + を適用すると、string から int への型変換が走り、10 が生まれる (コード量に頓着しないなら、1 を 10 個足し合わせてももちろん 10 が作れる)。

-

- また、error_reporting に指定しているのは -1 である。 これは、! によって文字列を false にし、+ によって false0 にし、さらにビット反転して -1 にしている。 + また、error_reporting に指定しているのは -1 である。 これは、! によって文字列を false にし、+ によって false0 にし、さらにビット反転して -1 にしている。

-
-

if 文なしで条件分岐

+

if 文なしで条件分岐

- 三項演算子ないし match 式を使うことで、if を一切書かずに条件分岐ができる。 また、&& / || も使えることがある。遅延評価が不要なケースでは、[$t, $f][$cond] のような形で分岐することもできる。 + 三項演算子ないし match 式を使うことで、if を一切書かずに条件分岐ができる。 また、&& / || も使えることがある。 遅延評価が不要なケースでは、[$t, $f][$cond] のような形で分岐することもできる。

-
-

whilefor 文なしでループ

+

whilefor 文なしでループ

- 不動点コンビネータを使って無名再帰する (詳しい説明は省略する。これらの単語で検索してほしい)。 ここでは、一般に Z コンビネータとして知られるものを使った ($z)。 + 不動点コンビネータを使って無名再帰する (詳しい説明は省略する。これらの単語で検索してほしい)。 ここでは、一般に Z コンビネータとして知られるものを使った ($z)。

-

- 実際のところ、$🤡$🎪$🐘 は、一度 Scheme (Lisp の一種) で書いてから PHP に翻訳する形で記述した。 + 実際のところ、$🤡$🎪$🐘 は、一度 Scheme (Lisp の一種) で書いてから PHP に翻訳する形で記述した。

-

- なお、PHP は末尾再帰の最適化をおこなわない (少なくとも今のところは) ので、 あまりに長い brainf*ck プログラムを書くとスタックオーバーフローする。 + なお、PHP は末尾再帰の最適化をおこなわない (少なくとも今のところは) ので、 あまりに長い brainf*ck プログラムを書くとスタックオーバーフローする。

-
-

第2問 riddle.php

+

第2問 riddle.php

- ソースコードはこちら。実行には PHP 8.0 以上が必要なので注意。 + ソースコードはこちら。実行には PHP 8.0 以上が必要なので注意。

-
<?php
 
@@ -372,111 +337,87 @@
   echo "{$x}\n\n";
 }
-

- さて、この問題はさきほどのように単純に実行しただけでは、謎のブロックが表示されるだけでトークンは得られない。トークンを得るためには、ソースコードを読み、定数 N を特定する必要がある。 + さて、この問題はさきほどのように単純に実行しただけでは、謎のブロックが表示されるだけでトークンは得られない。 トークンを得るためには、ソースコードを読み、定数 N を特定する必要がある。

-

- ここでは、私の想定解を解説する。 + ここでは、私の想定解を解説する。

-
-

読解

+

読解

- まずはソースコードを読んでいく。 + まずはソースコードを読んでいく。

-
$token = [
-// 略
+  // 略
 ];
-

- 数値からなる $token があり、各要素をループしている。 + 数値からなる $token があり、各要素をループしている。

-
$x = $x ^ N;
-

- まずは排他的論理和 (xor) を取り、 + まずは排他的論理和 (xor) を取り、

-
$x = sprintf('%025b', $x);
-

- 二進数に変換して、 + 二進数に変換して、

-
$x = str_replace(search: ['0', '1'], replace: [' ', '#'], subject: $x);
-

- 0 を空白に、1 を # にし、 + 0 を空白に、1 を # にし、

-
$x = implode("\n", str_split($x, length: 5));
-

- 5文字ごとに区切ったあと、改行で結合している。 + 5文字ごとに区切ったあと、改行で結合している。

-
-

ヒント

+

ヒント

- 次に、ソースコードに書いてあるヒントを読んでいく。 + 次に、ソースコードに書いてあるヒントを読んでいく。

-
  • N それ自体は、42 や 8128 といったような特別な意味を持たず、ランダムに決められている
  • -
  • $token の各要素は、1文字を表す
  • -
  • 1文字は 5x5 のセルからなる
  • -
  • 出力されるのは、完全な PHPer トークンである
-

- ここで、PHPer トークンは必ず # 記号から始まることを思いだすと、$token の最初の数字 0x14B499C は、変換の結果 # になるのではないかと予想される (なお、このことは、リポジトリの README ファイルに追加ヒントとして書かれている)。 + ここで、PHPer トークンは必ず # 記号から始まることを思いだすと、 $token の最初の数字 0x14B499C は、変換の結果 # になるのではないかと予想される (なお、このことは、リポジトリの README ファイルに追加ヒントとして書かれている)。

-
-

解く

+

解く

- ここまでわかれば、あと一歩で解ける。すなわち、0x14B499C# に変換されるような N を見つければよい。 + ここまでわかれば、あと一歩で解ける。すなわち、0x14B499C# に変換されるような N を見つければよい。

-

- N は高々 + N は高々

-
assert(0 <= N && N <= 0b11111_11111_11111_11111_11111);
-

- なのでブルートフォースしてもよいが、ここではブルートフォースしない方法を紹介する。 + なのでブルートフォースしてもよいが、ここではブルートフォースしない方法を紹介する。

-
<?php
 
@@ -495,11 +436,9 @@
 "#####\n" .
 " # # ");
-

- この一連の変換に対する逆変換を考えると、次のようになる。 + この一連の変換に対する逆変換を考えると、次のようになる。

-
<?php
 
@@ -518,19 +457,16 @@
 
 echo "N = $n\n";
-

- これを実行すると、N が得られる。 + これを実行すると、N が得られる。

-
-

第3問 toquine.php

+

第3問 toquine.php

- ソースコードはこちら。 + ソースコードはこちら。

-
<?php
 
@@ -562,71 +498,59 @@
 $ws = array_map(fn($w) => implode(', ', $w), array_chunk(array_map(fn($x) => sprintf('0x' . chr(37) . '07X', $x), $xs), 10));
 printf($s, $t, str_rot13("<<<'D'\n{$s}\nD"), implode(",\n", $ws));
-

- コメントにもあるとおり、次のようにして実行すれば答えがでてくる。 + コメントにもあるとおり、次のようにして実行すれば答えがでてくる。

-
$ php toquine.php | php | php | php | ...
-

- 実際にはもう少しパイプで繋げなければならない。 + 実際にはもう少しパイプで繋げなければならない。

-
-

解説

+

解説

-

プログラム全体

+

プログラム全体

- コメントにもあるとおり、これは quine (風) のプログラムになっている。Quine とは、自分のソースコードをそっくりそのまま出力するようなプログラムのことである。 + コメントにもあるとおり、これは quine (風) のプログラムになっている。 Quine とは、自分のソースコードをそっくりそのまま出力するようなプログラムのことである。

-

- このプログラムは、実行すると自身とほとんど同じプログラムを出力する。異なるのはトークンになっている部分のみである。 + このプログラムは、実行すると自身とほとんど同じプログラムを出力する。 異なるのはトークンになっている部分のみである。

-
-

トークン

+

トークン

$xs がトークンに対応している。変換のロジックは riddle.php とほぼ同じなので省略する。

-
-

状態保持

+

状態保持

- トークンの何文字目まで出力したかを、ソースコードを変えずに (quine なので) 覚えておく必要がある。このプログラムでは、トークンが出力されるとソースコードがだんだんと長くなっていくのを利用して、__LINE__ から情報を取得している。 + トークンの何文字目まで出力したかを、ソースコードを変えずに (quine なので) 覚えておく必要がある。 このプログラムでは、トークンが出力されるとソースコードがだんだんと長くなっていくのを利用して、__LINE__ から情報を取得している。

-
-

ROT 13

+

ROT 13

- Quine は、素朴に書くとプログラムの一部が 2回記述されてしまう。これがあまり美しくないので、toquine.php では、ROT 13 変換を使って難読化した。 + Quine は、素朴に書くとプログラムの一部が 2回記述されてしまう。 これがあまり美しくないので、toquine.php では、ROT 13 変換を使って難読化した。

-

- それにしてもなぜこんなものが標準ライブラリに……。 + それにしてもなぜこんなものが標準ライブラリに……。

-
-

おわりに

+

おわりに

- 解いていただいたみなさん、また、難易度調整につきあっていただいた社内のみなさん、ありがとうございました。 + 解いていただいたみなさん、また、難易度調整につきあっていただいた社内のみなさん、ありがとうございました。

-

- 今回は直前に作りはじめたのもあり、3問だけかつ使い古されたネタばかりになってしまいましたが、来年は 5問、より面白い問題を持っていきます。 + 今回は直前に作りはじめたのもあり、3問だけかつ使い古されたネタばかりになってしまいましたが、 来年は 5問、より面白い問題を持っていきます。

-

- 実はもう作りはじめているので、どうか来年もありますように……。 + 実はもう作りはじめているので、どうか来年もありますように……。

-- cgit v1.2.3-70-g09d2