From 5d5367ed00c22cc194b8a2411b2b4b828751003b Mon Sep 17 00:00:00 2001
From: nsfisis 本日 PHP カンファレンス沖縄 2022 が開催された (らしい)。 カンファレンスには参加できなかったものの、懇親会の LT で出題されたコードゴルフの問題が Twitter に流れてきたので、解いてみた。 ツイート: https://twitter.com/m3m0r7/status/1563397620231712772 ツイート: https://twitter.com/m3m0r7/status/1563397620231712772 細かいレギュレーションは不明だったので、勝手に定めた。 書いたものがこちら: しめて 123 バイトとなった (末尾改行を含めずにカウント)。 しめて 123 バイトとなった (末尾改行を含めずにカウント)。 こちらは改行とスペースを追加したバージョン: 割と多くの言語のゴルフで使えるテクニック。
+
スライド: https://speakerdeck.com/memory1994/php-conference-okinawa-2022-extra?slide=3解
[<?php $n=$argv[1];foreach([1e4,5e3,2e3,1e3,500,100,50,10,5,1]as$x)for(;$n>=$x;$n-=$x)$r[]=$x;echo implode(', ',$r??[]);?>]
-[<?php $n=$argv[1];foreach([1e4,5e3,2e3,1e3,500,100,50,10,5,1]as$x)for(;$n>=$x;$n-=$x)$r[]=$x;echo implode(', ',$r??[]);?>]
+[<?php
-
-$n = $argv[1];
-foreach ([1e4, 5e3, 2e3, 1e3, 500, 100, 50, 10, 5, 1] as $x)
- for (; $n >= $x; $n -= $x)
- $r[] = $x;
-echo implode(', ', $r ?? []);
-
-?>]
-使用したテクニック
+[<?php
+
+$n = $argv[1];
+foreach ([1e4, 5e3, 2e3, 1e3, 500, 100, 50, 10, 5, 1] as $x)
+ for (; $n >= $x; $n -= $x)
+ $r[] = $x;
+echo implode(', ', $r ?? []);
+
+?>]
+使用したテクニック
指数表記
e を用いた指数表記で、大きな数を短く表す。このコードでは 10000、5000、2000、1000 を指数表記している。foreach や for の中身を1つの文に
@@ -149,76 +149,76 @@ $n = $argv[1リポジトリはこちら: https://github.com/nsfisis/PHPerKaigi2022-tokens
ソースコードはこちら。実行には PHP 8.1 以上が必要なので注意。
-<?php
-
-declare(strict_types=0O1);
-
-namespace Dgcircus\PHPerKaigi\Y2022;
-
-/**
- * @todo
- * Run this program to acquire a PHPer token.
- */
-
-https://creativecommons.org/publicdomain/zero/1.0/
-
-\error_reporting(~+!'We are hiring!');
-
-$z = fn($f) => (fn($x) => $f(fn(...$xs) => $x($x)(...$xs)))(fn($x) => $f(fn(...$xs) => $x($x)(...$xs)));
-$id = \spl_object_id(...);
-$put = fn($c) => \printf('%c', $c);
-$mm = fn($p, $n) => new \ArrayObject(\array_fill(+!![], $n, $p));
-
-$👉 = fn($m, $p, $b, $e, $mp, $pc) => [++$mp, ++$pc];
-$👈 = fn($m, $p, $b, $e, $mp, $pc) => [--$mp, ++$pc];
-$👍 = fn($m, $p, $b, $e, $mp, $pc) => [$mp, ++$pc, ++$m[$mp]];
-$👎 = fn($m, $p, $b, $e, $mp, $pc) => [$mp, ++$pc, --$m[$mp]];
-$📝 = fn($m, $p, $b, $e, $mp, $pc) => [$mp, ++$pc, $put($m[$mp])];
-$🤡 = fn($m, $p, $b, $e, $mp, $pc) => match ($m[$mp]) {
- +!![] => [$mp, $z(fn($loop) => fn($pc, $n) => match ($id($p[$pc])) {
- $b => $loop(++$pc, ++$n),
- $e => $n === +!![] ? ++$pc : $loop(++$pc, --$n),
- default => $loop(++$pc, $n),
- })($pc, -![])],
- default => [$mp, ++$pc],
-};
-$🎪 = fn($m, $p, $b, $e, $mp, $pc) => match ($m[$mp]) {
- +!![] => [$mp, ++$pc],
- default => [$mp, $z(fn($loop) => fn($pc, $n) => match ($id($p[$pc])) {
- $e => $loop(--$pc, ++$n),
- $b => $n === +!![] ? $pc+![] : $loop(--$pc, --$n),
- default => $loop(--$pc, $n),
- })($pc, -![])],
-};
-$🐘 = fn($p) => $z(fn($loop) => fn($m, $p, $b, $e, $mp, $pc) =>
- isset($p[$pc]) && $loop($m, $p, $b, $e, ...($p[$pc]($m, $p, $b, $e, $mp, $pc)))
-)($mm(+!![], +(![].![])), $p, $id($🤡), $id($🎪), +!![], +!![]);
-
-$🐘([
- $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍,
- $🤡,
- $👉, $👍, $👍, $👍,
- $👉, $👍, $👍, $👍, $👍, $👍,
- $👉, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍,
- $👉, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍,
- $👈, $👈, $👈, $👈, $👎,
- $🎪,
- $👉, $👍, $👍, $👍, $👍, $👍, $📝,
- $👎, $👎, $📝,
- $👉, $👎, $👎, $👎, $📝,
- $👉, $👎, $👎, $👎, $📝,
- $👎, $👎, $📝,
- $👎, $📝,
- $👈, $📝,
- $👉, $👉, $👎, $👎, $📝,
- $👍, $👍, $👍, $👍, $👍, $👍, $👍, $📝,
- $👈, $👎, $👎, $👎, $👎, $📝,
- $👈, $📝,
- $👉, $👍, $👍, $📝,
- $👉, $👎, $📝,
- $👈, $📝,
-]);
-この問題は、単に適切なバージョンの PHP で動かせばトークンが得られる。
+<?php
+
+declare(strict_types=0O1);
+
+namespace Dgcircus\PHPerKaigi\Y2022;
+
+/**
+ * @todo
+ * Run this program to acquire a PHPer token.
+ */
+
+https://creativecommons.org/publicdomain/zero/1.0/
+
+\error_reporting(~+!'We are hiring!');
+
+$z = fn($f) => (fn($x) => $f(fn(...$xs) => $x($x)(...$xs)))(fn($x) => $f(fn(...$xs) => $x($x)(...$xs)));
+$id = \spl_object_id(...);
+$put = fn($c) => \printf('%c', $c);
+$mm = fn($p, $n) => new \ArrayObject(\array_fill(+!![], $n, $p));
+
+$👉 = fn($m, $p, $b, $e, $mp, $pc) => [++$mp, ++$pc];
+$👈 = fn($m, $p, $b, $e, $mp, $pc) => [--$mp, ++$pc];
+$👍 = fn($m, $p, $b, $e, $mp, $pc) => [$mp, ++$pc, ++$m[$mp]];
+$👎 = fn($m, $p, $b, $e, $mp, $pc) => [$mp, ++$pc, --$m[$mp]];
+$📝 = fn($m, $p, $b, $e, $mp, $pc) => [$mp, ++$pc, $put($m[$mp])];
+$🤡 = fn($m, $p, $b, $e, $mp, $pc) => match ($m[$mp]) {
+ +!![] => [$mp, $z(fn($loop) => fn($pc, $n) => match ($id($p[$pc])) {
+ $b => $loop(++$pc, ++$n),
+ $e => $n === +!![] ? ++$pc : $loop(++$pc, --$n),
+ default => $loop(++$pc, $n),
+ })($pc, -![])],
+ default => [$mp, ++$pc],
+};
+$🎪 = fn($m, $p, $b, $e, $mp, $pc) => match ($m[$mp]) {
+ +!![] => [$mp, ++$pc],
+ default => [$mp, $z(fn($loop) => fn($pc, $n) => match ($id($p[$pc])) {
+ $e => $loop(--$pc, ++$n),
+ $b => $n === +!![] ? $pc+![] : $loop(--$pc, --$n),
+ default => $loop(--$pc, $n),
+ })($pc, -![])],
+};
+$🐘 = fn($p) => $z(fn($loop) => fn($m, $p, $b, $e, $mp, $pc) =>
+ isset($p[$pc]) && $loop($m, $p, $b, $e, ...($p[$pc]($m, $p, $b, $e, $mp, $pc)))
+)($mm(+!![], +(![].![])), $p, $id($🤡), $id($🎪), +!![], +!![]);
+
+$🐘([
+ $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍,
+ $🤡,
+ $👉, $👍, $👍, $👍,
+ $👉, $👍, $👍, $👍, $👍, $👍,
+ $👉, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍,
+ $👉, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍,
+ $👈, $👈, $👈, $👈, $👎,
+ $🎪,
+ $👉, $👍, $👍, $👍, $👍, $👍, $📝,
+ $👎, $👎, $📝,
+ $👉, $👎, $👎, $👎, $📝,
+ $👉, $👎, $👎, $👎, $📝,
+ $👎, $👎, $📝,
+ $👎, $📝,
+ $👈, $📝,
+ $👉, $👉, $👎, $👎, $📝,
+ $👍, $👍, $👍, $👍, $👍, $👍, $👍, $📝,
+ $👈, $👎, $👎, $👎, $👎, $📝,
+ $👈, $📝,
+ $👉, $👍, $👍, $📝,
+ $👉, $👎, $📝,
+ $👈, $📝,
+]);
+この問題は、単に適切なバージョンの PHP で動かせばトークンが得られる。
まず目につくのは大量の絵文字だろう。
@@ -272,18 +272,18 @@ Brainf*ck とは、難解プログラミング言語のひとつであり、こ
今回は、PHP 8.1 から追加された、0O または 0o から始まる八進数リテラルを使った。
ソースコードのライセンスを示したこの部分だが、
-https://creativecommons.org/publicdomain/zero/1.0/
-完全に合法な PHP のコードである。 +
https://creativecommons.org/publicdomain/zero/1.0/
+完全に合法な PHP のコードである。
https: 部分はラベル、// 以降は行コメントになっている。
ソースコード中に、ほとんど数値リテラルが書かれていないことにお気づきだろうか。 PHP では、型変換を利用することで任意の整数を作り出すことができる。
-assert(0 === +!![]);
-assert(1 === +![]);
-assert(2 === ![]+![]);
-assert(3 === ![]+![]+![]);
-assert(10 === +(![].+!![]));
-[] に ! を適用すると true が返ってくる。それに +
+
assert(0 === +!![]);
+assert(1 === +![]);
+assert(2 === ![]+![]);
+assert(3 === ![]+![]+![]);
+assert(10 === +(![].+!![]));
+[] に ! を適用すると true が返ってくる。それに +
を適用すると、bool から int ヘの型変換が走り、1 が生成される。10
はさらにトリッキーだ。まず 1 と 0 を作り、. で文字列として結合する
('10')。これに + を適用すると、string から int
@@ -303,57 +303,57 @@ PHP では、型変換を利用することで任意の整数を作り出すこ
あまりに長い brainf*ck プログラムを書くとスタックオーバーフローする。
ソースコードはこちら。実行には PHP 8.0 以上が必要なので注意。
-<?php
-
-/*********************************************************
- * This program displays a PHPer token. *
- * Guess 'N'. *
- * *
- * Hints: *
- * - N itself has no special meaning, e.g., 42, 8128, *
- * it is selected at random. *
- * - Each element of $token represents a single letter. *
- * - One letter consists of 5x5 cells. *
- * - Remember, the output is a complete PHPer token. *
- * *
- * License: *
- * https://creativecommons.org/publicdomain/zero/1.0/ *
- *********************************************************/
-const N = 0 /* Change it to your answer. */;
-assert(0 <= N && N <= 0b11111_11111_11111_11111_11111);
-
-$token = [
- 0x14B499C,
- 0x0BE34CC, 0x01C9C69,
- 0x0ECA069, 0x01C2449, 0x0FDB166, 0x01C9C69,
- 0x01C1C66, 0x0FC1C47, 0x01C1C66,
- 0x10C5858, 0x1E4E3B8, 0x1A2F2F8,
-];
-foreach ($token as $x) {
- $x = $x ^ N;
-
- $x = sprintf('%025b', $x);
- $x = str_replace(search: ['0', '1'], replace: [' ', '#'], subject: $x);
- $x = implode("\n", str_split($x, length: 5));
- echo "{$x}\n\n";
-}
-さて、この問題はさきほどのように単純に実行しただけでは、謎のブロックが表示されるだけでトークンは得られない。 +
<?php
+
+/*********************************************************
+ * This program displays a PHPer token. *
+ * Guess 'N'. *
+ * *
+ * Hints: *
+ * - N itself has no special meaning, e.g., 42, 8128, *
+ * it is selected at random. *
+ * - Each element of $token represents a single letter. *
+ * - One letter consists of 5x5 cells. *
+ * - Remember, the output is a complete PHPer token. *
+ * *
+ * License: *
+ * https://creativecommons.org/publicdomain/zero/1.0/ *
+ *********************************************************/
+const N = 0 /* Change it to your answer. */;
+assert(0 <= N && N <= 0b11111_11111_11111_11111_11111);
+
+$token = [
+ 0x14B499C,
+ 0x0BE34CC, 0x01C9C69,
+ 0x0ECA069, 0x01C2449, 0x0FDB166, 0x01C9C69,
+ 0x01C1C66, 0x0FC1C47, 0x01C1C66,
+ 0x10C5858, 0x1E4E3B8, 0x1A2F2F8,
+];
+foreach ($token as $x) {
+ $x = $x ^ N;
+
+ $x = sprintf('%025b', $x);
+ $x = str_replace(search: ['0', '1'], replace: [' ', '#'], subject: $x);
+ $x = implode("\n", str_split($x, length: 5));
+ echo "{$x}\n\n";
+}
+さて、この問題はさきほどのように単純に実行しただけでは、謎のブロックが表示されるだけでトークンは得られない。
トークンを得るためには、ソースコードを読み、定数 N を特定する必要がある。
ここでは、私の想定解を解説する。
まずはソースコードを読んでいく。
-$token = [
- // 略
-];
-数値からなる $token があり、各要素をループしている。
$x = $x ^ N;
-まずは排他的論理和 (xor) を取り、
- $x = sprintf('%025b', $x);
-二進数に変換して、
- $x = str_replace(search: ['0', '1'], replace: [' ', '#'], subject: $x);
-0 を空白に、1 を # にし、
$x = implode("\n", str_split($x, length: 5));
-5文字ごとに区切ったあと、改行で結合している。
+$token = [
+ // 略
+];
+数値からなる $token があり、各要素をループしている。
$x = $x ^ N;
+まずは排他的論理和 (xor) を取り、
+ $x = sprintf('%025b', $x);
+二進数に変換して、
+ $x = str_replace(search: ['0', '1'], replace: [' ', '#'], subject: $x);
+0 を空白に、1 を # にし、
$x = implode("\n", str_split($x, length: 5));
+5文字ごとに区切ったあと、改行で結合している。
次に、ソースコードに書いてあるヒントを読んでいく。
ここまでわかれば、あと一歩で解ける。すなわち、0x14B499C が # に変換されるような N を見つければよい。
N は高々
assert(0 <= N && N <= 0b11111_11111_11111_11111_11111);
-なのでブルートフォースしてもよいが、ここではブルートフォースしない方法を紹介する。
-<?php
-
-$x = 0x14B499C;
-
-$x = $x ^ N;
-
-$x = sprintf('%025b', $x);
-$x = str_replace(search: ['0', '1'], replace: [' ', '#'], subject: $x);
-$x = implode("\n", str_split($x, length: 5));
-
-assert($x ===
- " # # \n" .
- "#####\n" .
- " # # \n" .
- "#####\n" .
- " # # ");
-この一連の変換に対する逆変換を考えると、次のようになる。
-<?php
-
-$x =
- " # # \n" .
- "#####\n" .
- " # # \n" .
- "#####\n" .
- " # # ";
-
-$x = implode('', explode("\n", $x));
-$x = str_replace(search: [' ', '#'], replace: ['0', '1'], subject: $x);
-$x = bindec($x);
-
-$n = $x ^ 0x14B499C;
-
-echo "N = $n\n";
-これを実行すると、N が得られる。
assert(0 <= N && N <= 0b11111_11111_11111_11111_11111);
+なのでブルートフォースしてもよいが、ここではブルートフォースしない方法を紹介する。
+<?php
+
+$x = 0x14B499C;
+
+$x = $x ^ N;
+
+$x = sprintf('%025b', $x);
+$x = str_replace(search: ['0', '1'], replace: [' ', '#'], subject: $x);
+$x = implode("\n", str_split($x, length: 5));
+
+assert($x ===
+ " # # \n" .
+ "#####\n" .
+ " # # \n" .
+ "#####\n" .
+ " # # ");
+この一連の変換に対する逆変換を考えると、次のようになる。
+<?php
+
+$x =
+ " # # \n" .
+ "#####\n" .
+ " # # \n" .
+ "#####\n" .
+ " # # ";
+
+$x = implode('', explode("\n", $x));
+$x = str_replace(search: [' ', '#'], replace: ['0', '1'], subject: $x);
+$x = bindec($x);
+
+$n = $x ^ 0x14B499C;
+
+echo "N = $n\n";
+これを実行すると、N が得られる。
ソースコードはこちら。
-<?php
-
-// License: https://creativecommons.org/publicdomain/zero/1.0/
-// This is a quine-like program to generate a PHPer token.
-// Execute it like this: php toquine.php | php | php | php | ...
-
-$s = <<<'Q'
-<?cuc
-// Yvprafr: uggcf://perngvirpbzzbaf.bet/choyvpqbznva/mreb/1.0/
-// Guvf vf n dhvar-yvxr cebtenz gb trarengr n CUCre gbxra.
-// Rkrphgr vg yvxr guvf: cuc gbdhvar.cuc | cuc | cuc | cuc | ...
-%f$f = %f;
-$f = fge_ebg13($f); $kf = [
-%f,
-];
-$g = ahyy.snyfr; sbe ($v = 0; $v <= vagqvi(__YVAR__-035,6); ++$v) vs (!vffrg($kf[$v])) oernx; ryfr
-$g .= vzcybqr("\a", fge_fcyvg(fge_ercynpr(['0','1'], [' ','##'], fcevags(pue(37) . '025o', $kf[$v])), 012)) . "\a\a";
-$jf = neenl_znc(sa($j) => vzcybqr(', ', $j), neenl_puhax(neenl_znc(sa($k) => fcevags('0k' . pue(37) . '07K', $k), $kf), 10));
-cevags($f, $g, fge_ebg13("<<<'Q'\a{$f}\aQ"), vzcybqr(",\a", $jf));
-Q;
-$s = str_rot13($s); $xs = [
-0x0AFABEA, 0x1F294A7, 0x1F2109F, 0x1F294A7, 0x0002800, 0x1F2109F, 0x0117041, 0x1F294A7, 0x1FAD6B5, 0x1F295B7,
-0x010FC21, 0x1FAD6B5, 0x1151151, 0x010FC21, 0x1F294A7, 0x1F295B7, 0x1FAD6B5, 0x1F294A7, 0x1F295B7, 0x1F8C63F,
-0x1F8C631, 0x1FAD6B5, 0x17AD6BD, 0x17AD6BD, 0x1F8C63F, 0x1F295B7,
-];
-$t = null.false; for ($i = 0; $i <= intdiv(__LINE__-035,6); ++$i) if (!isset($xs[$i])) break; else
-$t .= implode("\n", str_split(str_replace(['0','1'], [' ','##'], sprintf(chr(37) . '025b', $xs[$i])), 012)) . "\n\n";
-$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 | ...
-実際にはもう少しパイプで繋げなければならない。
+<?php
+
+// License: https://creativecommons.org/publicdomain/zero/1.0/
+// This is a quine-like program to generate a PHPer token.
+// Execute it like this: php toquine.php | php | php | php | ...
+
+$s = <<<'Q'
+<?cuc
+// Yvprafr: uggcf://perngvirpbzzbaf.bet/choyvpqbznva/mreb/1.0/
+// Guvf vf n dhvar-yvxr cebtenz gb trarengr n CUCre gbxra.
+// Rkrphgr vg yvxr guvf: cuc gbdhvar.cuc | cuc | cuc | cuc | ...
+%f$f = %f;
+$f = fge_ebg13($f); $kf = [
+%f,
+];
+$g = ahyy.snyfr; sbe ($v = 0; $v <= vagqvi(__YVAR__-035,6); ++$v) vs (!vffrg($kf[$v])) oernx; ryfr
+$g .= vzcybqr("\a", fge_fcyvg(fge_ercynpr(['0','1'], [' ','##'], fcevags(pue(37) . '025o', $kf[$v])), 012)) . "\a\a";
+$jf = neenl_znc(sa($j) => vzcybqr(', ', $j), neenl_puhax(neenl_znc(sa($k) => fcevags('0k' . pue(37) . '07K', $k), $kf), 10));
+cevags($f, $g, fge_ebg13("<<<'Q'\a{$f}\aQ"), vzcybqr(",\a", $jf));
+Q;
+$s = str_rot13($s); $xs = [
+0x0AFABEA, 0x1F294A7, 0x1F2109F, 0x1F294A7, 0x0002800, 0x1F2109F, 0x0117041, 0x1F294A7, 0x1FAD6B5, 0x1F295B7,
+0x010FC21, 0x1FAD6B5, 0x1151151, 0x010FC21, 0x1F294A7, 0x1F295B7, 0x1FAD6B5, 0x1F294A7, 0x1F295B7, 0x1F8C63F,
+0x1F8C631, 0x1FAD6B5, 0x17AD6BD, 0x17AD6BD, 0x1F8C63F, 0x1F295B7,
+];
+$t = null.false; for ($i = 0; $i <= intdiv(__LINE__-035,6); ++$i) if (!isset($xs[$i])) break; else
+$t .= implode("\n", str_split(str_replace(['0','1'], [' ','##'], sprintf(chr(37) . '025b', $xs[$i])), 012)) . "\n\n";
+$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 (風) のプログラムになっている。 diff --git a/docs/tags/conference/index.html b/docs/tags/conference/index.html index 1ff69f1..40c8675 100644 --- a/docs/tags/conference/index.html +++ b/docs/tags/conference/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/tags/conference/page/1/index.html b/docs/tags/conference/page/1/index.html index 0a923d6..740f29e 100644 --- a/docs/tags/conference/page/1/index.html +++ b/docs/tags/conference/page/1/index.html @@ -1 +1,10 @@ -
タイトル落ち。まずはこのコードを見て欲しい。
-#include <iostream>
-
-[[alignas]] [[alignof]] [[and]] [[and_eq]] [[asm]] [[auto]] [[bitand]]
-[[bitor]] [[bool]] [[break]] [[case]] [[catch]] [[char]] [[char16_t]]
-[[char32_t]] [[class]] [[compl]] [[const]] [[const_cast]] [[constexpr]]
-[[continue]] [[decltype]] [[default]] [[delete]] [[do]] [[double]]
-[[dynamic_cast]] [[else]] [[enum]] [[explicit]] [[export]] [[extern]] [[false]]
-[[final]] [[float]] [[for]] [[friend]] [[goto]] [[if]] [[inline]] [[int]]
-[[long]] [[mutable]] [[namespace]] [[new]] [[noexcept]] [[not]] [[not_eq]]
-[[nullptr]] [[operator]] [[or]] [[or_eq]] [[override]] [[private]]
-[[protected]] [[public]] [[register]] [[reinterpret_cast]] [[return]] [[short]]
-[[signed]] [[sizeof]] [[static]] [[static_assert]] [[static_cast]] [[struct]]
-[[switch]] [[template]] [[this]] [[thread_local]] [[throw]] [[true]] [[try]]
-[[typedef]] [[typeid]] [[typename]] [[union]] [[unsigned]]
-[[virtual]] [[void]] [[volatile]] [[wchar_t]] [[while]] [[xor]] [[xor_eq]]
-// [[using]]
-int main() {
- std::cout << "Hello, World!" << std::endl;
-}
-+#include <iostream> + +[[alignas]] [[alignof]] [[and]] [[and_eq]] [[asm]] [[auto]] [[bitand]] +[[bitor]] [[bool]] [[break]] [[case]] [[catch]] [[char]] [[char16_t]] +[[char32_t]] [[class]] [[compl]] [[const]] [[const_cast]] [[constexpr]] +[[continue]] [[decltype]] [[default]] [[delete]] [[do]] [[double]] +[[dynamic_cast]] [[else]] [[enum]] [[explicit]] [[export]] [[extern]] [[false]] +[[final]] [[float]] [[for]] [[friend]] [[goto]] [[if]] [[inline]] [[int]] +[[long]] [[mutable]] [[namespace]] [[new]] [[noexcept]] [[not]] [[not_eq]] +[[nullptr]] [[operator]] [[or]] [[or_eq]] [[override]] [[private]] +[[protected]] [[public]] [[register]] [[reinterpret_cast]] [[return]] [[short]] +[[signed]] [[sizeof]] [[static]] [[static_assert]] [[static_cast]] [[struct]] +[[switch]] [[template]] [[this]] [[thread_local]] [[throw]] [[true]] [[try]] +[[typedef]] [[typeid]] [[typename]] [[union]] [[unsigned]] +[[virtual]] [[void]] [[volatile]] [[wchar_t]] [[while]] [[xor]] [[xor_eq]] +// [[using]] +int main() { + std::cout << "Hello, World!" << std::endl; +} +コンパイラのバージョン $ clang++ –version Apple clang version 11.0.0 (clang-1100.0.33.8) @@ -62,9 +62,9 @@ $ clang++ –std=c++17 hoge.cpp
同サイトの [keywords のページ] (https://en.cppreference.com/w/cpp/keyword) から一覧を拝借し、上のコードが出来上がった (C++17 においてキーワードでないものなど、一部省いている)。 大量の警告 (unknown attribute ‘〇〇’ ignored) がコンパイラから出力されるが、コンパイルできる。
上のコードでは
-[[using]]をコメントアウトしているが、これはusingキーワードのみ属性構文の中で意味を持つからであり、このコメントアウトを外すとコンパイルに失敗する。// using の例 -[[using foo: attr1, attr2]] int x; // [[foo::attr1, foo::attr2]] の糖衣構文 -C++17 の仕様も見てみる (正確には標準化前のドラフト)。
+// using の例 +[[using foo: attr1, attr2]] int x; // [[foo::attr1, foo::attr2]] の糖衣構文 +C++17 の仕様も見てみる (正確には標準化前のドラフト)。
引用元: https://timsong-cpp.github.io/cppwp/n4659/dcl.attr#grammar-4
If a keyword or an alternative token that satisfies the syntactic requirements of an identifier is contained in an attribute-token, it is considered an identifier.
diff --git a/docs/tags/cpp/index.html b/docs/tags/cpp/index.html index 40b8fd8..73b29b1 100644 --- a/docs/tags/cpp/index.html +++ b/docs/tags/cpp/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/tags/cpp/page/1/index.html b/docs/tags/cpp/page/1/index.html index c7ce5f2..ee45690 100644 --- a/docs/tags/cpp/page/1/index.html +++ b/docs/tags/cpp/page/1/index.html @@ -1 +1,10 @@ -https://blog.nsfisis.dev/tags/cpp/ \ No newline at end of file + + + +https://blog.nsfisis.dev/tags/cpp/ + + + + + + diff --git a/docs/tags/cpp17/feed.xml b/docs/tags/cpp17/feed.xml index b1e9aa9..b1be398 100644 --- a/docs/tags/cpp17/feed.xml +++ b/docs/tags/cpp17/feed.xml @@ -17,26 +17,26 @@ 元 URL: https://qiita.com/nsfisis/items/94090937bcf860cfa93b
タイトル落ち。まずはこのコードを見て欲しい。
-#include <iostream> - -[[alignas]] [[alignof]] [[and]] [[and_eq]] [[asm]] [[auto]] [[bitand]] -[[bitor]] [[bool]] [[break]] [[case]] [[catch]] [[char]] [[char16_t]] -[[char32_t]] [[class]] [[compl]] [[const]] [[const_cast]] [[constexpr]] -[[continue]] [[decltype]] [[default]] [[delete]] [[do]] [[double]] -[[dynamic_cast]] [[else]] [[enum]] [[explicit]] [[export]] [[extern]] [[false]] -[[final]] [[float]] [[for]] [[friend]] [[goto]] [[if]] [[inline]] [[int]] -[[long]] [[mutable]] [[namespace]] [[new]] [[noexcept]] [[not]] [[not_eq]] -[[nullptr]] [[operator]] [[or]] [[or_eq]] [[override]] [[private]] -[[protected]] [[public]] [[register]] [[reinterpret_cast]] [[return]] [[short]] -[[signed]] [[sizeof]] [[static]] [[static_assert]] [[static_cast]] [[struct]] -[[switch]] [[template]] [[this]] [[thread_local]] [[throw]] [[true]] [[try]] -[[typedef]] [[typeid]] [[typename]] [[union]] [[unsigned]] -[[virtual]] [[void]] [[volatile]] [[wchar_t]] [[while]] [[xor]] [[xor_eq]] -// [[using]] -int main() { - std::cout << "Hello, World!" << std::endl; -} -+#include <iostream> + +[[alignas]] [[alignof]] [[and]] [[and_eq]] [[asm]] [[auto]] [[bitand]] +[[bitor]] [[bool]] [[break]] [[case]] [[catch]] [[char]] [[char16_t]] +[[char32_t]] [[class]] [[compl]] [[const]] [[const_cast]] [[constexpr]] +[[continue]] [[decltype]] [[default]] [[delete]] [[do]] [[double]] +[[dynamic_cast]] [[else]] [[enum]] [[explicit]] [[export]] [[extern]] [[false]] +[[final]] [[float]] [[for]] [[friend]] [[goto]] [[if]] [[inline]] [[int]] +[[long]] [[mutable]] [[namespace]] [[new]] [[noexcept]] [[not]] [[not_eq]] +[[nullptr]] [[operator]] [[or]] [[or_eq]] [[override]] [[private]] +[[protected]] [[public]] [[register]] [[reinterpret_cast]] [[return]] [[short]] +[[signed]] [[sizeof]] [[static]] [[static_assert]] [[static_cast]] [[struct]] +[[switch]] [[template]] [[this]] [[thread_local]] [[throw]] [[true]] [[try]] +[[typedef]] [[typeid]] [[typename]] [[union]] [[unsigned]] +[[virtual]] [[void]] [[volatile]] [[wchar_t]] [[while]] [[xor]] [[xor_eq]] +// [[using]] +int main() { + std::cout << "Hello, World!" << std::endl; +} +コンパイラのバージョン $ clang++ –version Apple clang version 11.0.0 (clang-1100.0.33.8) @@ -62,9 +62,9 @@ $ clang++ –std=c++17 hoge.cpp
同サイトの [keywords のページ] (https://en.cppreference.com/w/cpp/keyword) から一覧を拝借し、上のコードが出来上がった (C++17 においてキーワードでないものなど、一部省いている)。 大量の警告 (unknown attribute ‘〇〇’ ignored) がコンパイラから出力されるが、コンパイルできる。
上のコードでは
-[[using]]をコメントアウトしているが、これはusingキーワードのみ属性構文の中で意味を持つからであり、このコメントアウトを外すとコンパイルに失敗する。// using の例 -[[using foo: attr1, attr2]] int x; // [[foo::attr1, foo::attr2]] の糖衣構文 -C++17 の仕様も見てみる (正確には標準化前のドラフト)。
+// using の例 +[[using foo: attr1, attr2]] int x; // [[foo::attr1, foo::attr2]] の糖衣構文 +C++17 の仕様も見てみる (正確には標準化前のドラフト)。
引用元: https://timsong-cpp.github.io/cppwp/n4659/dcl.attr#grammar-4
If a keyword or an alternative token that satisfies the syntactic requirements of an identifier is contained in an attribute-token, it is considered an identifier.
diff --git a/docs/tags/cpp17/index.html b/docs/tags/cpp17/index.html index 0b728b1..3dd9d68 100644 --- a/docs/tags/cpp17/index.html +++ b/docs/tags/cpp17/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/tags/cpp17/page/1/index.html b/docs/tags/cpp17/page/1/index.html index fbe22d9..74e12d4 100644 --- a/docs/tags/cpp17/page/1/index.html +++ b/docs/tags/cpp17/page/1/index.html @@ -1 +1,10 @@ -https://blog.nsfisis.dev/tags/cpp17/ \ No newline at end of file + + + +https://blog.nsfisis.dev/tags/cpp17/ + + + + + + diff --git a/docs/tags/index.html b/docs/tags/index.html index 8978816..79ef62b 100644 --- a/docs/tags/index.html +++ b/docs/tags/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/tags/my-programs/feed.xml b/docs/tags/my-programs/feed.xml index 4d4ca44..34cd4bb 100644 --- a/docs/tags/my-programs/feed.xml +++ b/docs/tags/my-programs/feed.xml @@ -15,7 +15,7 @@https://blog.nsfisis.dev/posts/2022-04-24/term-banner-write-tool-showing-banner-in-terminal/ はじめに こんなものを作った。
-$ term-banner 'Hello, World!' 'こんにちは、' '世界!' +$ term-banner 'Hello, World!' 'こんにちは、' '世界!'
コマンドライン引数として渡した文字列をターミナルに大きく表示する。
リポジトリはこちら: https://github.com/nsfisis/term-banner
diff --git a/docs/tags/my-programs/index.html b/docs/tags/my-programs/index.html index 22420a7..bc71599 100644 --- a/docs/tags/my-programs/index.html +++ b/docs/tags/my-programs/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/tags/my-programs/page/1/index.html b/docs/tags/my-programs/page/1/index.html index 7aedb41..36f7d78 100644 --- a/docs/tags/my-programs/page/1/index.html +++ b/docs/tags/my-programs/page/1/index.html @@ -1 +1,10 @@ -https://blog.nsfisis.dev/tags/my-programs/ \ No newline at end of file + + + +https://blog.nsfisis.dev/tags/my-programs/ + + + + + + diff --git a/docs/tags/page/1/index.html b/docs/tags/page/1/index.html index 71c6610..2dbd4ae 100644 --- a/docs/tags/page/1/index.html +++ b/docs/tags/page/1/index.html @@ -1 +1,10 @@ -https://blog.nsfisis.dev/tags/ \ No newline at end of file + + + +https://blog.nsfisis.dev/tags/ + + + + + + diff --git a/docs/tags/php/feed.xml b/docs/tags/php/feed.xml index 5b39f4f..248d3ec 100644 --- a/docs/tags/php/feed.xml +++ b/docs/tags/php/feed.xml @@ -16,7 +16,7 @@はじめに diff --git a/docs/tags/python3/index.html b/docs/tags/python3/index.html index a59912a..5e18ffd 100644 --- a/docs/tags/python3/index.html +++ b/docs/tags/python3/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/tags/python3/page/1/index.html b/docs/tags/python3/page/1/index.html index 2e5623f..1c8bbe3 100644 --- a/docs/tags/python3/page/1/index.html +++ b/docs/tags/python3/page/1/index.html @@ -1 +1,10 @@ -本日 PHP カンファレンス沖縄 2022 が開催された (らしい)。
カンファレンスには参加できなかったものの、懇親会の LT で出題されたコードゴルフの問題が Twitter に流れてきたので、解いてみた。
-ツイート: https://twitter.com/m3m0r7/status/1563397620231712772
+ツイート: https://twitter.com/m3m0r7/status/1563397620231712772
スライド: https://speakerdeck.com/memory1994/php-conference-okinawa-2022-extra?slide=3解
細かいレギュレーションは不明だったので、勝手に定めた。
@@ -29,19 +29,19 @@負数は入ってこないものとする 書いたものがこちら:
-[<?php $n=$argv[1];foreach([1e4,5e3,2e3,1e3,500,100,50,10,5,1]as$x)for(;$n>=$x;$n-=$x)$r[]=$x;echo implode(', ',$r??[]);?>] -しめて 123 バイトとなった (末尾改行を含めずにカウント)。
+[<?php $n=$argv[1];foreach([1e4,5e3,2e3,1e3,500,100,50,10,5,1]as$x)for(;$n>=$x;$n-=$x)$r[]=$x;echo implode(', ',$r??[]);?>] +しめて 123 バイトとなった (末尾改行を含めずにカウント)。
こちらは改行とスペースを追加したバージョン:
-[<?php - -$n = $argv[1]; -foreach ([1e4, 5e3, 2e3, 1e3, 500, 100, 50, 10, 5, 1] as $x) - for (; $n >= $x; $n -= $x) - $r[] = $x; -echo implode(', ', $r ?? []); - -?>] -使用したテクニック
+[<?php + +$n = $argv[1]; +foreach ([1e4, 5e3, 2e3, 1e3, 500, 100, 50, 10, 5, 1] as $x) + for (; $n >= $x; $n -= $x) + $r[] = $x; +echo implode(', ', $r ?? []); + +?>] +使用したテクニック
指数表記
割と多くの言語のゴルフで使えるテクニック。
eを用いた指数表記で、大きな数を短く表す。このコードでは10000、5000、2000、1000を指数表記している。foreach や for の中身を1つの文に
@@ -149,76 +149,76 @@ $n = $argv[1リポジトリはこちら: https://github.com/nsfisis/PHPerKaigi2022-tokens第1問 brainf_ck.php
ソースコードはこちら。実行には PHP 8.1 以上が必要なので注意。
-<?php - -declare(strict_types=0O1); - -namespace Dgcircus\PHPerKaigi\Y2022; - -/** - * @todo - * Run this program to acquire a PHPer token. - */ - -https://creativecommons.org/publicdomain/zero/1.0/ - -\error_reporting(~+!'We are hiring!'); - -$z = fn($f) => (fn($x) => $f(fn(...$xs) => $x($x)(...$xs)))(fn($x) => $f(fn(...$xs) => $x($x)(...$xs))); -$id = \spl_object_id(...); -$put = fn($c) => \printf('%c', $c); -$mm = fn($p, $n) => new \ArrayObject(\array_fill(+!![], $n, $p)); - -$👉 = fn($m, $p, $b, $e, $mp, $pc) => [++$mp, ++$pc]; -$👈 = fn($m, $p, $b, $e, $mp, $pc) => [--$mp, ++$pc]; -$👍 = fn($m, $p, $b, $e, $mp, $pc) => [$mp, ++$pc, ++$m[$mp]]; -$👎 = fn($m, $p, $b, $e, $mp, $pc) => [$mp, ++$pc, --$m[$mp]]; -$📝 = fn($m, $p, $b, $e, $mp, $pc) => [$mp, ++$pc, $put($m[$mp])]; -$🤡 = fn($m, $p, $b, $e, $mp, $pc) => match ($m[$mp]) { - +!![] => [$mp, $z(fn($loop) => fn($pc, $n) => match ($id($p[$pc])) { - $b => $loop(++$pc, ++$n), - $e => $n === +!![] ? ++$pc : $loop(++$pc, --$n), - default => $loop(++$pc, $n), - })($pc, -![])], - default => [$mp, ++$pc], -}; -$🎪 = fn($m, $p, $b, $e, $mp, $pc) => match ($m[$mp]) { - +!![] => [$mp, ++$pc], - default => [$mp, $z(fn($loop) => fn($pc, $n) => match ($id($p[$pc])) { - $e => $loop(--$pc, ++$n), - $b => $n === +!![] ? $pc+![] : $loop(--$pc, --$n), - default => $loop(--$pc, $n), - })($pc, -![])], -}; -$🐘 = fn($p) => $z(fn($loop) => fn($m, $p, $b, $e, $mp, $pc) => - isset($p[$pc]) && $loop($m, $p, $b, $e, ...($p[$pc]($m, $p, $b, $e, $mp, $pc))) -)($mm(+!![], +(![].![])), $p, $id($🤡), $id($🎪), +!![], +!![]); - -$🐘([ - $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, - $🤡, - $👉, $👍, $👍, $👍, - $👉, $👍, $👍, $👍, $👍, $👍, - $👉, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, - $👉, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, - $👈, $👈, $👈, $👈, $👎, - $🎪, - $👉, $👍, $👍, $👍, $👍, $👍, $📝, - $👎, $👎, $📝, - $👉, $👎, $👎, $👎, $📝, - $👉, $👎, $👎, $👎, $📝, - $👎, $👎, $📝, - $👎, $📝, - $👈, $📝, - $👉, $👉, $👎, $👎, $📝, - $👍, $👍, $👍, $👍, $👍, $👍, $👍, $📝, - $👈, $👎, $👎, $👎, $👎, $📝, - $👈, $📝, - $👉, $👍, $👍, $📝, - $👉, $👎, $📝, - $👈, $📝, -]); -この問題は、単に適切なバージョンの PHP で動かせばトークンが得られる。
+<?php + +declare(strict_types=0O1); + +namespace Dgcircus\PHPerKaigi\Y2022; + +/** + * @todo + * Run this program to acquire a PHPer token. + */ + +https://creativecommons.org/publicdomain/zero/1.0/ + +\error_reporting(~+!'We are hiring!'); + +$z = fn($f) => (fn($x) => $f(fn(...$xs) => $x($x)(...$xs)))(fn($x) => $f(fn(...$xs) => $x($x)(...$xs))); +$id = \spl_object_id(...); +$put = fn($c) => \printf('%c', $c); +$mm = fn($p, $n) => new \ArrayObject(\array_fill(+!![], $n, $p)); + +$👉 = fn($m, $p, $b, $e, $mp, $pc) => [++$mp, ++$pc]; +$👈 = fn($m, $p, $b, $e, $mp, $pc) => [--$mp, ++$pc]; +$👍 = fn($m, $p, $b, $e, $mp, $pc) => [$mp, ++$pc, ++$m[$mp]]; +$👎 = fn($m, $p, $b, $e, $mp, $pc) => [$mp, ++$pc, --$m[$mp]]; +$📝 = fn($m, $p, $b, $e, $mp, $pc) => [$mp, ++$pc, $put($m[$mp])]; +$🤡 = fn($m, $p, $b, $e, $mp, $pc) => match ($m[$mp]) { + +!![] => [$mp, $z(fn($loop) => fn($pc, $n) => match ($id($p[$pc])) { + $b => $loop(++$pc, ++$n), + $e => $n === +!![] ? ++$pc : $loop(++$pc, --$n), + default => $loop(++$pc, $n), + })($pc, -![])], + default => [$mp, ++$pc], +}; +$🎪 = fn($m, $p, $b, $e, $mp, $pc) => match ($m[$mp]) { + +!![] => [$mp, ++$pc], + default => [$mp, $z(fn($loop) => fn($pc, $n) => match ($id($p[$pc])) { + $e => $loop(--$pc, ++$n), + $b => $n === +!![] ? $pc+![] : $loop(--$pc, --$n), + default => $loop(--$pc, $n), + })($pc, -![])], +}; +$🐘 = fn($p) => $z(fn($loop) => fn($m, $p, $b, $e, $mp, $pc) => + isset($p[$pc]) && $loop($m, $p, $b, $e, ...($p[$pc]($m, $p, $b, $e, $mp, $pc))) +)($mm(+!![], +(![].![])), $p, $id($🤡), $id($🎪), +!![], +!![]); + +$🐘([ + $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, + $🤡, + $👉, $👍, $👍, $👍, + $👉, $👍, $👍, $👍, $👍, $👍, + $👉, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, + $👉, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, + $👈, $👈, $👈, $👈, $👎, + $🎪, + $👉, $👍, $👍, $👍, $👍, $👍, $📝, + $👎, $👎, $📝, + $👉, $👎, $👎, $👎, $📝, + $👉, $👎, $👎, $👎, $📝, + $👎, $👎, $📝, + $👎, $📝, + $👈, $📝, + $👉, $👉, $👎, $👎, $📝, + $👍, $👍, $👍, $👍, $👍, $👍, $👍, $📝, + $👈, $👎, $👎, $👎, $👎, $📝, + $👈, $📝, + $👉, $👍, $👍, $📝, + $👉, $👎, $📝, + $👈, $📝, +]); +この問題は、単に適切なバージョンの PHP で動かせばトークンが得られる。
解説
絵文字
まず目につくのは大量の絵文字だろう。 @@ -272,18 +272,18 @@ Brainf*ck とは、難解プログラミング言語のひとつであり、こ 今回は、PHP 8.1 から追加された、
0Oまたは0oから始まる八進数リテラルを使った。URL
ソースコードのライセンスを示したこの部分だが、
-https://creativecommons.org/publicdomain/zero/1.0/ -完全に合法な PHP のコードである。 +
https://creativecommons.org/publicdomain/zero/1.0/ +完全に合法な PHP のコードである。
https:部分はラベル、//以降は行コメントになっている。リテラルなしで数値を生成する
ソースコード中に、ほとんど数値リテラルが書かれていないことにお気づきだろうか。 PHP では、型変換を利用することで任意の整数を作り出すことができる。
-assert(0 === +!![]); -assert(1 === +![]); -assert(2 === ![]+![]); -assert(3 === ![]+![]+![]); -assert(10 === +(![].+!![])); -
[]に!を適用するとtrueが返ってくる。それに++assert(0 === +!![]); +assert(1 === +![]); +assert(2 === ![]+![]); +assert(3 === ![]+![]+![]); +assert(10 === +(![].+!![])); +
[]に!を適用するとtrueが返ってくる。それに+を適用すると、boolからintヘの型変換が走り、1が生成される。10はさらにトリッキーだ。まず1と0を作り、.で文字列として結合する ('10')。これに+を適用すると、stringからint@@ -303,57 +303,57 @@ PHP では、型変換を利用することで任意の整数を作り出すこ あまりに長い brainf*ck プログラムを書くとスタックオーバーフローする。第2問 riddle.php
ソースコードはこちら。実行には PHP 8.0 以上が必要なので注意。
-<?php - -/********************************************************* - * This program displays a PHPer token. * - * Guess 'N'. * - * * - * Hints: * - * - N itself has no special meaning, e.g., 42, 8128, * - * it is selected at random. * - * - Each element of $token represents a single letter. * - * - One letter consists of 5x5 cells. * - * - Remember, the output is a complete PHPer token. * - * * - * License: * - * https://creativecommons.org/publicdomain/zero/1.0/ * - *********************************************************/ -const N = 0 /* Change it to your answer. */; -assert(0 <= N && N <= 0b11111_11111_11111_11111_11111); - -$token = [ - 0x14B499C, - 0x0BE34CC, 0x01C9C69, - 0x0ECA069, 0x01C2449, 0x0FDB166, 0x01C9C69, - 0x01C1C66, 0x0FC1C47, 0x01C1C66, - 0x10C5858, 0x1E4E3B8, 0x1A2F2F8, -]; -foreach ($token as $x) { - $x = $x ^ N; - - $x = sprintf('%025b', $x); - $x = str_replace(search: ['0', '1'], replace: [' ', '#'], subject: $x); - $x = implode("\n", str_split($x, length: 5)); - echo "{$x}\n\n"; -} -さて、この問題はさきほどのように単純に実行しただけでは、謎のブロックが表示されるだけでトークンは得られない。 +
<?php + +/********************************************************* + * This program displays a PHPer token. * + * Guess 'N'. * + * * + * Hints: * + * - N itself has no special meaning, e.g., 42, 8128, * + * it is selected at random. * + * - Each element of $token represents a single letter. * + * - One letter consists of 5x5 cells. * + * - Remember, the output is a complete PHPer token. * + * * + * License: * + * https://creativecommons.org/publicdomain/zero/1.0/ * + *********************************************************/ +const N = 0 /* Change it to your answer. */; +assert(0 <= N && N <= 0b11111_11111_11111_11111_11111); + +$token = [ + 0x14B499C, + 0x0BE34CC, 0x01C9C69, + 0x0ECA069, 0x01C2449, 0x0FDB166, 0x01C9C69, + 0x01C1C66, 0x0FC1C47, 0x01C1C66, + 0x10C5858, 0x1E4E3B8, 0x1A2F2F8, +]; +foreach ($token as $x) { + $x = $x ^ N; + + $x = sprintf('%025b', $x); + $x = str_replace(search: ['0', '1'], replace: [' ', '#'], subject: $x); + $x = implode("\n", str_split($x, length: 5)); + echo "{$x}\n\n"; +} +さて、この問題はさきほどのように単純に実行しただけでは、謎のブロックが表示されるだけでトークンは得られない。 トークンを得るためには、ソースコードを読み、定数
Nを特定する必要がある。ここでは、私の想定解を解説する。
読解
まずはソースコードを読んでいく。
-$token = [ - // 略 -]; -数値からなる
-$tokenがあり、各要素をループしている。$x = $x ^ N; -まずは排他的論理和 (xor) を取り、
-$x = sprintf('%025b', $x); -二進数に変換して、
-$x = str_replace(search: ['0', '1'], replace: [' ', '#'], subject: $x); -0 を空白に、1 を
-#にし、$x = implode("\n", str_split($x, length: 5)); -5文字ごとに区切ったあと、改行で結合している。
+$token = [ + // 略 +]; +数値からなる
+$tokenがあり、各要素をループしている。$x = $x ^ N; +まずは排他的論理和 (xor) を取り、
+$x = sprintf('%025b', $x); +二進数に変換して、
+$x = str_replace(search: ['0', '1'], replace: [' ', '#'], subject: $x); +0 を空白に、1 を
+#にし、$x = implode("\n", str_split($x, length: 5)); +5文字ごとに区切ったあと、改行で結合している。
ヒント
次に、ソースコードに書いてあるヒントを読んでいく。
@@ -367,76 +367,76 @@ $token = [
解く
ここまでわかれば、あと一歩で解ける。すなわち、
0x14B499Cが#に変換されるようなNを見つければよい。-
Nは高々assert(0 <= N && N <= 0b11111_11111_11111_11111_11111); -なのでブルートフォースしてもよいが、ここではブルートフォースしない方法を紹介する。
-<?php - -$x = 0x14B499C; - -$x = $x ^ N; - -$x = sprintf('%025b', $x); -$x = str_replace(search: ['0', '1'], replace: [' ', '#'], subject: $x); -$x = implode("\n", str_split($x, length: 5)); - -assert($x === - " # # \n" . - "#####\n" . - " # # \n" . - "#####\n" . - " # # "); -この一連の変換に対する逆変換を考えると、次のようになる。
-<?php - -$x = - " # # \n" . - "#####\n" . - " # # \n" . - "#####\n" . - " # # "; - -$x = implode('', explode("\n", $x)); -$x = str_replace(search: [' ', '#'], replace: ['0', '1'], subject: $x); -$x = bindec($x); - -$n = $x ^ 0x14B499C; - -echo "N = $n\n"; -これを実行すると、
+Nが得られる。assert(0 <= N && N <= 0b11111_11111_11111_11111_11111); +なのでブルートフォースしてもよいが、ここではブルートフォースしない方法を紹介する。
+<?php + +$x = 0x14B499C; + +$x = $x ^ N; + +$x = sprintf('%025b', $x); +$x = str_replace(search: ['0', '1'], replace: [' ', '#'], subject: $x); +$x = implode("\n", str_split($x, length: 5)); + +assert($x === + " # # \n" . + "#####\n" . + " # # \n" . + "#####\n" . + " # # "); +この一連の変換に対する逆変換を考えると、次のようになる。
+<?php + +$x = + " # # \n" . + "#####\n" . + " # # \n" . + "#####\n" . + " # # "; + +$x = implode('', explode("\n", $x)); +$x = str_replace(search: [' ', '#'], replace: ['0', '1'], subject: $x); +$x = bindec($x); + +$n = $x ^ 0x14B499C; + +echo "N = $n\n"; +これを実行すると、
Nが得られる。第3問 toquine.php
ソースコードはこちら。
-<?php - -// License: https://creativecommons.org/publicdomain/zero/1.0/ -// This is a quine-like program to generate a PHPer token. -// Execute it like this: php toquine.php | php | php | php | ... - -$s = <<<'Q' -<?cuc -// Yvprafr: uggcf://perngvirpbzzbaf.bet/choyvpqbznva/mreb/1.0/ -// Guvf vf n dhvar-yvxr cebtenz gb trarengr n CUCre gbxra. -// Rkrphgr vg yvxr guvf: cuc gbdhvar.cuc | cuc | cuc | cuc | ... -%f$f = %f; -$f = fge_ebg13($f); $kf = [ -%f, -]; -$g = ahyy.snyfr; sbe ($v = 0; $v <= vagqvi(__YVAR__-035,6); ++$v) vs (!vffrg($kf[$v])) oernx; ryfr -$g .= vzcybqr("\a", fge_fcyvg(fge_ercynpr(['0','1'], [' ','##'], fcevags(pue(37) . '025o', $kf[$v])), 012)) . "\a\a"; -$jf = neenl_znc(sa($j) => vzcybqr(', ', $j), neenl_puhax(neenl_znc(sa($k) => fcevags('0k' . pue(37) . '07K', $k), $kf), 10)); -cevags($f, $g, fge_ebg13("<<<'Q'\a{$f}\aQ"), vzcybqr(",\a", $jf)); -Q; -$s = str_rot13($s); $xs = [ -0x0AFABEA, 0x1F294A7, 0x1F2109F, 0x1F294A7, 0x0002800, 0x1F2109F, 0x0117041, 0x1F294A7, 0x1FAD6B5, 0x1F295B7, -0x010FC21, 0x1FAD6B5, 0x1151151, 0x010FC21, 0x1F294A7, 0x1F295B7, 0x1FAD6B5, 0x1F294A7, 0x1F295B7, 0x1F8C63F, -0x1F8C631, 0x1FAD6B5, 0x17AD6BD, 0x17AD6BD, 0x1F8C63F, 0x1F295B7, -]; -$t = null.false; for ($i = 0; $i <= intdiv(__LINE__-035,6); ++$i) if (!isset($xs[$i])) break; else -$t .= implode("\n", str_split(str_replace(['0','1'], [' ','##'], sprintf(chr(37) . '025b', $xs[$i])), 012)) . "\n\n"; -$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 | ... -実際にはもう少しパイプで繋げなければならない。
+<?php + +// License: https://creativecommons.org/publicdomain/zero/1.0/ +// This is a quine-like program to generate a PHPer token. +// Execute it like this: php toquine.php | php | php | php | ... + +$s = <<<'Q' +<?cuc +// Yvprafr: uggcf://perngvirpbzzbaf.bet/choyvpqbznva/mreb/1.0/ +// Guvf vf n dhvar-yvxr cebtenz gb trarengr n CUCre gbxra. +// Rkrphgr vg yvxr guvf: cuc gbdhvar.cuc | cuc | cuc | cuc | ... +%f$f = %f; +$f = fge_ebg13($f); $kf = [ +%f, +]; +$g = ahyy.snyfr; sbe ($v = 0; $v <= vagqvi(__YVAR__-035,6); ++$v) vs (!vffrg($kf[$v])) oernx; ryfr +$g .= vzcybqr("\a", fge_fcyvg(fge_ercynpr(['0','1'], [' ','##'], fcevags(pue(37) . '025o', $kf[$v])), 012)) . "\a\a"; +$jf = neenl_znc(sa($j) => vzcybqr(', ', $j), neenl_puhax(neenl_znc(sa($k) => fcevags('0k' . pue(37) . '07K', $k), $kf), 10)); +cevags($f, $g, fge_ebg13("<<<'Q'\a{$f}\aQ"), vzcybqr(",\a", $jf)); +Q; +$s = str_rot13($s); $xs = [ +0x0AFABEA, 0x1F294A7, 0x1F2109F, 0x1F294A7, 0x0002800, 0x1F2109F, 0x0117041, 0x1F294A7, 0x1FAD6B5, 0x1F295B7, +0x010FC21, 0x1FAD6B5, 0x1151151, 0x010FC21, 0x1F294A7, 0x1F295B7, 0x1FAD6B5, 0x1F294A7, 0x1F295B7, 0x1F8C63F, +0x1F8C631, 0x1FAD6B5, 0x17AD6BD, 0x17AD6BD, 0x1F8C63F, 0x1F295B7, +]; +$t = null.false; for ($i = 0; $i <= intdiv(__LINE__-035,6); ++$i) if (!isset($xs[$i])) break; else +$t .= implode("\n", str_split(str_replace(['0','1'], [' ','##'], sprintf(chr(37) . '025b', $xs[$i])), 012)) . "\n\n"; +$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 (風) のプログラムになっている。 diff --git a/docs/tags/php/index.html b/docs/tags/php/index.html index c949caf..bb2338f 100644 --- a/docs/tags/php/index.html +++ b/docs/tags/php/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/tags/php/page/1/index.html b/docs/tags/php/page/1/index.html index abae162..9d866e9 100644 --- a/docs/tags/php/page/1/index.html +++ b/docs/tags/php/page/1/index.html @@ -1 +1,10 @@ -
https://blog.nsfisis.dev/tags/php/ \ No newline at end of file + + + +https://blog.nsfisis.dev/tags/php/ + + + + + + diff --git a/docs/tags/phpcon/feed.xml b/docs/tags/phpcon/feed.xml index 69ebe09..b5b8325 100644 --- a/docs/tags/phpcon/feed.xml +++ b/docs/tags/phpcon/feed.xml @@ -16,7 +16,7 @@はじめに diff --git a/docs/tags/python/index.html b/docs/tags/python/index.html index 2551526..884872e 100644 --- a/docs/tags/python/index.html +++ b/docs/tags/python/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/tags/python/page/1/index.html b/docs/tags/python/page/1/index.html index 9560b32..ca045d7 100644 --- a/docs/tags/python/page/1/index.html +++ b/docs/tags/python/page/1/index.html @@ -1 +1,10 @@ -本日 PHP カンファレンス沖縄 2022 が開催された (らしい)。
カンファレンスには参加できなかったものの、懇親会の LT で出題されたコードゴルフの問題が Twitter に流れてきたので、解いてみた。
-ツイート: https://twitter.com/m3m0r7/status/1563397620231712772
+ツイート: https://twitter.com/m3m0r7/status/1563397620231712772
スライド: https://speakerdeck.com/memory1994/php-conference-okinawa-2022-extra?slide=3解
細かいレギュレーションは不明だったので、勝手に定めた。
@@ -29,19 +29,19 @@負数は入ってこないものとする 書いたものがこちら:
-[<?php $n=$argv[1];foreach([1e4,5e3,2e3,1e3,500,100,50,10,5,1]as$x)for(;$n>=$x;$n-=$x)$r[]=$x;echo implode(', ',$r??[]);?>] -しめて 123 バイトとなった (末尾改行を含めずにカウント)。
+[<?php $n=$argv[1];foreach([1e4,5e3,2e3,1e3,500,100,50,10,5,1]as$x)for(;$n>=$x;$n-=$x)$r[]=$x;echo implode(', ',$r??[]);?>] +しめて 123 バイトとなった (末尾改行を含めずにカウント)。
こちらは改行とスペースを追加したバージョン:
-[<?php - -$n = $argv[1]; -foreach ([1e4, 5e3, 2e3, 1e3, 500, 100, 50, 10, 5, 1] as $x) - for (; $n >= $x; $n -= $x) - $r[] = $x; -echo implode(', ', $r ?? []); - -?>] -使用したテクニック
+[<?php + +$n = $argv[1]; +foreach ([1e4, 5e3, 2e3, 1e3, 500, 100, 50, 10, 5, 1] as $x) + for (; $n >= $x; $n -= $x) + $r[] = $x; +echo implode(', ', $r ?? []); + +?>] +使用したテクニック
指数表記
割と多くの言語のゴルフで使えるテクニック。
eを用いた指数表記で、大きな数を短く表す。このコードでは10000、5000、2000、1000を指数表記している。foreach や for の中身を1つの文に
diff --git a/docs/tags/phpcon/index.html b/docs/tags/phpcon/index.html index 637e229..d3d3cba 100644 --- a/docs/tags/phpcon/index.html +++ b/docs/tags/phpcon/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/tags/phpcon/page/1/index.html b/docs/tags/phpcon/page/1/index.html index 05742cd..736f0cb 100644 --- a/docs/tags/phpcon/page/1/index.html +++ b/docs/tags/phpcon/page/1/index.html @@ -1 +1,10 @@ -https://blog.nsfisis.dev/tags/phpcon/ \ No newline at end of file + + + +https://blog.nsfisis.dev/tags/phpcon/ + + + + + + diff --git a/docs/tags/phperkaigi/feed.xml b/docs/tags/phperkaigi/feed.xml index a750f1e..9c19c75 100644 --- a/docs/tags/phperkaigi/feed.xml +++ b/docs/tags/phperkaigi/feed.xml @@ -99,76 +99,76 @@リポジトリはこちら: https://github.com/nsfisis/PHPerKaigi2022-tokens
第1問 brainf_ck.php
ソースコードはこちら。実行には PHP 8.1 以上が必要なので注意。
-<?php - -declare(strict_types=0O1); - -namespace Dgcircus\PHPerKaigi\Y2022; - -/** - * @todo - * Run this program to acquire a PHPer token. - */ - -https://creativecommons.org/publicdomain/zero/1.0/ - -\error_reporting(~+!'We are hiring!'); - -$z = fn($f) => (fn($x) => $f(fn(...$xs) => $x($x)(...$xs)))(fn($x) => $f(fn(...$xs) => $x($x)(...$xs))); -$id = \spl_object_id(...); -$put = fn($c) => \printf('%c', $c); -$mm = fn($p, $n) => new \ArrayObject(\array_fill(+!![], $n, $p)); - -$👉 = fn($m, $p, $b, $e, $mp, $pc) => [++$mp, ++$pc]; -$👈 = fn($m, $p, $b, $e, $mp, $pc) => [--$mp, ++$pc]; -$👍 = fn($m, $p, $b, $e, $mp, $pc) => [$mp, ++$pc, ++$m[$mp]]; -$👎 = fn($m, $p, $b, $e, $mp, $pc) => [$mp, ++$pc, --$m[$mp]]; -$📝 = fn($m, $p, $b, $e, $mp, $pc) => [$mp, ++$pc, $put($m[$mp])]; -$🤡 = fn($m, $p, $b, $e, $mp, $pc) => match ($m[$mp]) { - +!![] => [$mp, $z(fn($loop) => fn($pc, $n) => match ($id($p[$pc])) { - $b => $loop(++$pc, ++$n), - $e => $n === +!![] ? ++$pc : $loop(++$pc, --$n), - default => $loop(++$pc, $n), - })($pc, -![])], - default => [$mp, ++$pc], -}; -$🎪 = fn($m, $p, $b, $e, $mp, $pc) => match ($m[$mp]) { - +!![] => [$mp, ++$pc], - default => [$mp, $z(fn($loop) => fn($pc, $n) => match ($id($p[$pc])) { - $e => $loop(--$pc, ++$n), - $b => $n === +!![] ? $pc+![] : $loop(--$pc, --$n), - default => $loop(--$pc, $n), - })($pc, -![])], -}; -$🐘 = fn($p) => $z(fn($loop) => fn($m, $p, $b, $e, $mp, $pc) => - isset($p[$pc]) && $loop($m, $p, $b, $e, ...($p[$pc]($m, $p, $b, $e, $mp, $pc))) -)($mm(+!![], +(![].![])), $p, $id($🤡), $id($🎪), +!![], +!![]); - -$🐘([ - $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, - $🤡, - $👉, $👍, $👍, $👍, - $👉, $👍, $👍, $👍, $👍, $👍, - $👉, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, - $👉, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, - $👈, $👈, $👈, $👈, $👎, - $🎪, - $👉, $👍, $👍, $👍, $👍, $👍, $📝, - $👎, $👎, $📝, - $👉, $👎, $👎, $👎, $📝, - $👉, $👎, $👎, $👎, $📝, - $👎, $👎, $📝, - $👎, $📝, - $👈, $📝, - $👉, $👉, $👎, $👎, $📝, - $👍, $👍, $👍, $👍, $👍, $👍, $👍, $📝, - $👈, $👎, $👎, $👎, $👎, $📝, - $👈, $📝, - $👉, $👍, $👍, $📝, - $👉, $👎, $📝, - $👈, $📝, -]); -この問題は、単に適切なバージョンの PHP で動かせばトークンが得られる。
+<?php + +declare(strict_types=0O1); + +namespace Dgcircus\PHPerKaigi\Y2022; + +/** + * @todo + * Run this program to acquire a PHPer token. + */ + +https://creativecommons.org/publicdomain/zero/1.0/ + +\error_reporting(~+!'We are hiring!'); + +$z = fn($f) => (fn($x) => $f(fn(...$xs) => $x($x)(...$xs)))(fn($x) => $f(fn(...$xs) => $x($x)(...$xs))); +$id = \spl_object_id(...); +$put = fn($c) => \printf('%c', $c); +$mm = fn($p, $n) => new \ArrayObject(\array_fill(+!![], $n, $p)); + +$👉 = fn($m, $p, $b, $e, $mp, $pc) => [++$mp, ++$pc]; +$👈 = fn($m, $p, $b, $e, $mp, $pc) => [--$mp, ++$pc]; +$👍 = fn($m, $p, $b, $e, $mp, $pc) => [$mp, ++$pc, ++$m[$mp]]; +$👎 = fn($m, $p, $b, $e, $mp, $pc) => [$mp, ++$pc, --$m[$mp]]; +$📝 = fn($m, $p, $b, $e, $mp, $pc) => [$mp, ++$pc, $put($m[$mp])]; +$🤡 = fn($m, $p, $b, $e, $mp, $pc) => match ($m[$mp]) { + +!![] => [$mp, $z(fn($loop) => fn($pc, $n) => match ($id($p[$pc])) { + $b => $loop(++$pc, ++$n), + $e => $n === +!![] ? ++$pc : $loop(++$pc, --$n), + default => $loop(++$pc, $n), + })($pc, -![])], + default => [$mp, ++$pc], +}; +$🎪 = fn($m, $p, $b, $e, $mp, $pc) => match ($m[$mp]) { + +!![] => [$mp, ++$pc], + default => [$mp, $z(fn($loop) => fn($pc, $n) => match ($id($p[$pc])) { + $e => $loop(--$pc, ++$n), + $b => $n === +!![] ? $pc+![] : $loop(--$pc, --$n), + default => $loop(--$pc, $n), + })($pc, -![])], +}; +$🐘 = fn($p) => $z(fn($loop) => fn($m, $p, $b, $e, $mp, $pc) => + isset($p[$pc]) && $loop($m, $p, $b, $e, ...($p[$pc]($m, $p, $b, $e, $mp, $pc))) +)($mm(+!![], +(![].![])), $p, $id($🤡), $id($🎪), +!![], +!![]); + +$🐘([ + $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, + $🤡, + $👉, $👍, $👍, $👍, + $👉, $👍, $👍, $👍, $👍, $👍, + $👉, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, + $👉, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, $👍, + $👈, $👈, $👈, $👈, $👎, + $🎪, + $👉, $👍, $👍, $👍, $👍, $👍, $📝, + $👎, $👎, $📝, + $👉, $👎, $👎, $👎, $📝, + $👉, $👎, $👎, $👎, $📝, + $👎, $👎, $📝, + $👎, $📝, + $👈, $📝, + $👉, $👉, $👎, $👎, $📝, + $👍, $👍, $👍, $👍, $👍, $👍, $👍, $📝, + $👈, $👎, $👎, $👎, $👎, $📝, + $👈, $📝, + $👉, $👍, $👍, $📝, + $👉, $👎, $📝, + $👈, $📝, +]); +この問題は、単に適切なバージョンの PHP で動かせばトークンが得られる。
解説
絵文字
まず目につくのは大量の絵文字だろう。 @@ -222,18 +222,18 @@ Brainf*ck とは、難解プログラミング言語のひとつであり、こ 今回は、PHP 8.1 から追加された、
0Oまたは0oから始まる八進数リテラルを使った。URL
ソースコードのライセンスを示したこの部分だが、
-https://creativecommons.org/publicdomain/zero/1.0/ -完全に合法な PHP のコードである。 +
https://creativecommons.org/publicdomain/zero/1.0/ +完全に合法な PHP のコードである。
https:部分はラベル、//以降は行コメントになっている。リテラルなしで数値を生成する
ソースコード中に、ほとんど数値リテラルが書かれていないことにお気づきだろうか。 PHP では、型変換を利用することで任意の整数を作り出すことができる。
-assert(0 === +!![]); -assert(1 === +![]); -assert(2 === ![]+![]); -assert(3 === ![]+![]+![]); -assert(10 === +(![].+!![])); -
[]に!を適用するとtrueが返ってくる。それに++assert(0 === +!![]); +assert(1 === +![]); +assert(2 === ![]+![]); +assert(3 === ![]+![]+![]); +assert(10 === +(![].+!![])); +
[]に!を適用するとtrueが返ってくる。それに+を適用すると、boolからintヘの型変換が走り、1が生成される。10はさらにトリッキーだ。まず1と0を作り、.で文字列として結合する ('10')。これに+を適用すると、stringからint@@ -253,57 +253,57 @@ PHP では、型変換を利用することで任意の整数を作り出すこ あまりに長い brainf*ck プログラムを書くとスタックオーバーフローする。第2問 riddle.php
ソースコードはこちら。実行には PHP 8.0 以上が必要なので注意。
-<?php - -/********************************************************* - * This program displays a PHPer token. * - * Guess 'N'. * - * * - * Hints: * - * - N itself has no special meaning, e.g., 42, 8128, * - * it is selected at random. * - * - Each element of $token represents a single letter. * - * - One letter consists of 5x5 cells. * - * - Remember, the output is a complete PHPer token. * - * * - * License: * - * https://creativecommons.org/publicdomain/zero/1.0/ * - *********************************************************/ -const N = 0 /* Change it to your answer. */; -assert(0 <= N && N <= 0b11111_11111_11111_11111_11111); - -$token = [ - 0x14B499C, - 0x0BE34CC, 0x01C9C69, - 0x0ECA069, 0x01C2449, 0x0FDB166, 0x01C9C69, - 0x01C1C66, 0x0FC1C47, 0x01C1C66, - 0x10C5858, 0x1E4E3B8, 0x1A2F2F8, -]; -foreach ($token as $x) { - $x = $x ^ N; - - $x = sprintf('%025b', $x); - $x = str_replace(search: ['0', '1'], replace: [' ', '#'], subject: $x); - $x = implode("\n", str_split($x, length: 5)); - echo "{$x}\n\n"; -} -さて、この問題はさきほどのように単純に実行しただけでは、謎のブロックが表示されるだけでトークンは得られない。 +
<?php + +/********************************************************* + * This program displays a PHPer token. * + * Guess 'N'. * + * * + * Hints: * + * - N itself has no special meaning, e.g., 42, 8128, * + * it is selected at random. * + * - Each element of $token represents a single letter. * + * - One letter consists of 5x5 cells. * + * - Remember, the output is a complete PHPer token. * + * * + * License: * + * https://creativecommons.org/publicdomain/zero/1.0/ * + *********************************************************/ +const N = 0 /* Change it to your answer. */; +assert(0 <= N && N <= 0b11111_11111_11111_11111_11111); + +$token = [ + 0x14B499C, + 0x0BE34CC, 0x01C9C69, + 0x0ECA069, 0x01C2449, 0x0FDB166, 0x01C9C69, + 0x01C1C66, 0x0FC1C47, 0x01C1C66, + 0x10C5858, 0x1E4E3B8, 0x1A2F2F8, +]; +foreach ($token as $x) { + $x = $x ^ N; + + $x = sprintf('%025b', $x); + $x = str_replace(search: ['0', '1'], replace: [' ', '#'], subject: $x); + $x = implode("\n", str_split($x, length: 5)); + echo "{$x}\n\n"; +} +さて、この問題はさきほどのように単純に実行しただけでは、謎のブロックが表示されるだけでトークンは得られない。 トークンを得るためには、ソースコードを読み、定数
Nを特定する必要がある。ここでは、私の想定解を解説する。
読解
まずはソースコードを読んでいく。
-$token = [ - // 略 -]; -数値からなる
-$tokenがあり、各要素をループしている。$x = $x ^ N; -まずは排他的論理和 (xor) を取り、
-$x = sprintf('%025b', $x); -二進数に変換して、
-$x = str_replace(search: ['0', '1'], replace: [' ', '#'], subject: $x); -0 を空白に、1 を
-#にし、$x = implode("\n", str_split($x, length: 5)); -5文字ごとに区切ったあと、改行で結合している。
+$token = [ + // 略 +]; +数値からなる
+$tokenがあり、各要素をループしている。$x = $x ^ N; +まずは排他的論理和 (xor) を取り、
+$x = sprintf('%025b', $x); +二進数に変換して、
+$x = str_replace(search: ['0', '1'], replace: [' ', '#'], subject: $x); +0 を空白に、1 を
+#にし、$x = implode("\n", str_split($x, length: 5)); +5文字ごとに区切ったあと、改行で結合している。
ヒント
次に、ソースコードに書いてあるヒントを読んでいく。
@@ -317,76 +317,76 @@ $token = [
解く
ここまでわかれば、あと一歩で解ける。すなわち、
0x14B499Cが#に変換されるようなNを見つければよい。-
Nは高々assert(0 <= N && N <= 0b11111_11111_11111_11111_11111); -なのでブルートフォースしてもよいが、ここではブルートフォースしない方法を紹介する。
-<?php - -$x = 0x14B499C; - -$x = $x ^ N; - -$x = sprintf('%025b', $x); -$x = str_replace(search: ['0', '1'], replace: [' ', '#'], subject: $x); -$x = implode("\n", str_split($x, length: 5)); - -assert($x === - " # # \n" . - "#####\n" . - " # # \n" . - "#####\n" . - " # # "); -この一連の変換に対する逆変換を考えると、次のようになる。
-<?php - -$x = - " # # \n" . - "#####\n" . - " # # \n" . - "#####\n" . - " # # "; - -$x = implode('', explode("\n", $x)); -$x = str_replace(search: [' ', '#'], replace: ['0', '1'], subject: $x); -$x = bindec($x); - -$n = $x ^ 0x14B499C; - -echo "N = $n\n"; -これを実行すると、
+Nが得られる。assert(0 <= N && N <= 0b11111_11111_11111_11111_11111); +なのでブルートフォースしてもよいが、ここではブルートフォースしない方法を紹介する。
+<?php + +$x = 0x14B499C; + +$x = $x ^ N; + +$x = sprintf('%025b', $x); +$x = str_replace(search: ['0', '1'], replace: [' ', '#'], subject: $x); +$x = implode("\n", str_split($x, length: 5)); + +assert($x === + " # # \n" . + "#####\n" . + " # # \n" . + "#####\n" . + " # # "); +この一連の変換に対する逆変換を考えると、次のようになる。
+<?php + +$x = + " # # \n" . + "#####\n" . + " # # \n" . + "#####\n" . + " # # "; + +$x = implode('', explode("\n", $x)); +$x = str_replace(search: [' ', '#'], replace: ['0', '1'], subject: $x); +$x = bindec($x); + +$n = $x ^ 0x14B499C; + +echo "N = $n\n"; +これを実行すると、
Nが得られる。第3問 toquine.php
ソースコードはこちら。
-<?php - -// License: https://creativecommons.org/publicdomain/zero/1.0/ -// This is a quine-like program to generate a PHPer token. -// Execute it like this: php toquine.php | php | php | php | ... - -$s = <<<'Q' -<?cuc -// Yvprafr: uggcf://perngvirpbzzbaf.bet/choyvpqbznva/mreb/1.0/ -// Guvf vf n dhvar-yvxr cebtenz gb trarengr n CUCre gbxra. -// Rkrphgr vg yvxr guvf: cuc gbdhvar.cuc | cuc | cuc | cuc | ... -%f$f = %f; -$f = fge_ebg13($f); $kf = [ -%f, -]; -$g = ahyy.snyfr; sbe ($v = 0; $v <= vagqvi(__YVAR__-035,6); ++$v) vs (!vffrg($kf[$v])) oernx; ryfr -$g .= vzcybqr("\a", fge_fcyvg(fge_ercynpr(['0','1'], [' ','##'], fcevags(pue(37) . '025o', $kf[$v])), 012)) . "\a\a"; -$jf = neenl_znc(sa($j) => vzcybqr(', ', $j), neenl_puhax(neenl_znc(sa($k) => fcevags('0k' . pue(37) . '07K', $k), $kf), 10)); -cevags($f, $g, fge_ebg13("<<<'Q'\a{$f}\aQ"), vzcybqr(",\a", $jf)); -Q; -$s = str_rot13($s); $xs = [ -0x0AFABEA, 0x1F294A7, 0x1F2109F, 0x1F294A7, 0x0002800, 0x1F2109F, 0x0117041, 0x1F294A7, 0x1FAD6B5, 0x1F295B7, -0x010FC21, 0x1FAD6B5, 0x1151151, 0x010FC21, 0x1F294A7, 0x1F295B7, 0x1FAD6B5, 0x1F294A7, 0x1F295B7, 0x1F8C63F, -0x1F8C631, 0x1FAD6B5, 0x17AD6BD, 0x17AD6BD, 0x1F8C63F, 0x1F295B7, -]; -$t = null.false; for ($i = 0; $i <= intdiv(__LINE__-035,6); ++$i) if (!isset($xs[$i])) break; else -$t .= implode("\n", str_split(str_replace(['0','1'], [' ','##'], sprintf(chr(37) . '025b', $xs[$i])), 012)) . "\n\n"; -$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 | ... -実際にはもう少しパイプで繋げなければならない。
+<?php + +// License: https://creativecommons.org/publicdomain/zero/1.0/ +// This is a quine-like program to generate a PHPer token. +// Execute it like this: php toquine.php | php | php | php | ... + +$s = <<<'Q' +<?cuc +// Yvprafr: uggcf://perngvirpbzzbaf.bet/choyvpqbznva/mreb/1.0/ +// Guvf vf n dhvar-yvxr cebtenz gb trarengr n CUCre gbxra. +// Rkrphgr vg yvxr guvf: cuc gbdhvar.cuc | cuc | cuc | cuc | ... +%f$f = %f; +$f = fge_ebg13($f); $kf = [ +%f, +]; +$g = ahyy.snyfr; sbe ($v = 0; $v <= vagqvi(__YVAR__-035,6); ++$v) vs (!vffrg($kf[$v])) oernx; ryfr +$g .= vzcybqr("\a", fge_fcyvg(fge_ercynpr(['0','1'], [' ','##'], fcevags(pue(37) . '025o', $kf[$v])), 012)) . "\a\a"; +$jf = neenl_znc(sa($j) => vzcybqr(', ', $j), neenl_puhax(neenl_znc(sa($k) => fcevags('0k' . pue(37) . '07K', $k), $kf), 10)); +cevags($f, $g, fge_ebg13("<<<'Q'\a{$f}\aQ"), vzcybqr(",\a", $jf)); +Q; +$s = str_rot13($s); $xs = [ +0x0AFABEA, 0x1F294A7, 0x1F2109F, 0x1F294A7, 0x0002800, 0x1F2109F, 0x0117041, 0x1F294A7, 0x1FAD6B5, 0x1F295B7, +0x010FC21, 0x1FAD6B5, 0x1151151, 0x010FC21, 0x1F294A7, 0x1F295B7, 0x1FAD6B5, 0x1F294A7, 0x1F295B7, 0x1F8C63F, +0x1F8C631, 0x1FAD6B5, 0x17AD6BD, 0x17AD6BD, 0x1F8C63F, 0x1F295B7, +]; +$t = null.false; for ($i = 0; $i <= intdiv(__LINE__-035,6); ++$i) if (!isset($xs[$i])) break; else +$t .= implode("\n", str_split(str_replace(['0','1'], [' ','##'], sprintf(chr(37) . '025b', $xs[$i])), 012)) . "\n\n"; +$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 (風) のプログラムになっている。 diff --git a/docs/tags/phperkaigi/index.html b/docs/tags/phperkaigi/index.html index 7da5581..ae48d05 100644 --- a/docs/tags/phperkaigi/index.html +++ b/docs/tags/phperkaigi/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/tags/phperkaigi/page/1/index.html b/docs/tags/phperkaigi/page/1/index.html index ecafc09..38c704d 100644 --- a/docs/tags/phperkaigi/page/1/index.html +++ b/docs/tags/phperkaigi/page/1/index.html @@ -1 +1,10 @@ -
https://blog.nsfisis.dev/tags/phperkaigi/ \ No newline at end of file + + + +https://blog.nsfisis.dev/tags/phperkaigi/ + + + + + + diff --git a/docs/tags/python/feed.xml b/docs/tags/python/feed.xml index 68dfe16..3b8ba29 100644 --- a/docs/tags/python/feed.xml +++ b/docs/tags/python/feed.xml @@ -18,38 +18,38 @@
本記事は Python 3.7.6 の動作結果を元にして書かれている。
Python でクロージャを作ろうと、次のようなコードを書いた。
-def f(): - x = 0 - def g(): - x += 1 - g() - -f() -関数
gから 関数fのスコープ内で定義された変数xを参照し、それに 1 を足そうとしている。 +def f(): + x = 0 + def g(): + x += 1 + g() + +f() +関数
gから 関数fのスコープ内で定義された変数xを参照し、それに 1 を足そうとしている。 これを実行するとx += 1の箇所でエラーが発生する。UnboundLocalError: local variable ‘x’ referenced before assignment
local変数
-xが代入前に参照された、とある。これは、fのxを参照するのではなく、新しく別の変数をg内に作ってしまっているため。 前述のコードを宣言と代入を便宜上分けて書き直すと次のようになる。varを変数宣言のための構文として擬似的に利用している。# 注: var は正しい Python の文法ではない。上記参照のこと -def f(): - var x # f の local変数 'x' を宣言 - x = 0 # x に 0 を代入 - def g(): # f の内部関数 g を定義 - var x # g の local変数 'x' を宣言 - # たまたま f にも同じ名前の変数があるが、それとは別の変数 - x += 1 # x に 1 を加算 (x = x + 1 の糖衣構文) - # 加算する前の値を参照しようとするが、まだ代入されていないためエラー - g() -当初の意図を表現するには、次のように書けばよい。
-def f(): - x = 0 - def g(): - nonlocal x ## (*) - x += 1 - g() -+
(*)のように、nonlocalを追加する。これにより一つ外側のスコープ (gの一つ外側 =f) で定義されているxを探しに行くようになる。# 注: var は正しい Python の文法ではない。上記参照のこと +def f(): + var x # f の local変数 'x' を宣言 + x = 0 # x に 0 を代入 + def g(): # f の内部関数 g を定義 + var x # g の local変数 'x' を宣言 + # たまたま f にも同じ名前の変数があるが、それとは別の変数 + x += 1 # x に 1 を加算 (x = x + 1 の糖衣構文) + # 加算する前の値を参照しようとするが、まだ代入されていないためエラー + g() +当初の意図を表現するには、次のように書けばよい。
+def f(): + x = 0 + def g(): + nonlocal x ## (*) + x += 1 + g() +]]>
(*)のように、nonlocalを追加する。これにより一つ外側のスコープ (gの一つ外側 =f) で定義されているxを探しに行くようになる。https://blog.nsfisis.dev/tags/python/ \ No newline at end of file + + + +https://blog.nsfisis.dev/tags/python/ + + + + + + diff --git a/docs/tags/python3/feed.xml b/docs/tags/python3/feed.xml index 114063d..260f94a 100644 --- a/docs/tags/python3/feed.xml +++ b/docs/tags/python3/feed.xml @@ -18,38 +18,38 @@
本記事は Python 3.7.6 の動作結果を元にして書かれている。
Python でクロージャを作ろうと、次のようなコードを書いた。
-def f(): - x = 0 - def g(): - x += 1 - g() - -f() -関数
gから 関数fのスコープ内で定義された変数xを参照し、それに 1 を足そうとしている。 +def f(): + x = 0 + def g(): + x += 1 + g() + +f() +関数
gから 関数fのスコープ内で定義された変数xを参照し、それに 1 を足そうとしている。 これを実行するとx += 1の箇所でエラーが発生する。UnboundLocalError: local variable ‘x’ referenced before assignment
local変数
-xが代入前に参照された、とある。これは、fのxを参照するのではなく、新しく別の変数をg内に作ってしまっているため。 前述のコードを宣言と代入を便宜上分けて書き直すと次のようになる。varを変数宣言のための構文として擬似的に利用している。# 注: var は正しい Python の文法ではない。上記参照のこと -def f(): - var x # f の local変数 'x' を宣言 - x = 0 # x に 0 を代入 - def g(): # f の内部関数 g を定義 - var x # g の local変数 'x' を宣言 - # たまたま f にも同じ名前の変数があるが、それとは別の変数 - x += 1 # x に 1 を加算 (x = x + 1 の糖衣構文) - # 加算する前の値を参照しようとするが、まだ代入されていないためエラー - g() -当初の意図を表現するには、次のように書けばよい。
-def f(): - x = 0 - def g(): - nonlocal x ## (*) - x += 1 - g() -+
(*)のように、nonlocalを追加する。これにより一つ外側のスコープ (gの一つ外側 =f) で定義されているxを探しに行くようになる。# 注: var は正しい Python の文法ではない。上記参照のこと +def f(): + var x # f の local変数 'x' を宣言 + x = 0 # x に 0 を代入 + def g(): # f の内部関数 g を定義 + var x # g の local変数 'x' を宣言 + # たまたま f にも同じ名前の変数があるが、それとは別の変数 + x += 1 # x に 1 を加算 (x = x + 1 の糖衣構文) + # 加算する前の値を参照しようとするが、まだ代入されていないためエラー + g() +当初の意図を表現するには、次のように書けばよい。
+def f(): + x = 0 + def g(): + nonlocal x ## (*) + x += 1 + g() +]]>
(*)のように、nonlocalを追加する。これにより一つ外側のスコープ (gの一つ外側 =f) で定義されているxを探しに行くようになる。https://blog.nsfisis.dev/tags/python3/ \ No newline at end of file + + + +https://blog.nsfisis.dev/tags/python3/ + + + + + + diff --git a/docs/tags/ruby/feed.xml b/docs/tags/ruby/feed.xml index a2923c1..50612e5 100644 --- a/docs/tags/ruby/feed.xml +++ b/docs/tags/ruby/feed.xml @@ -20,61 +20,61 @@
case-inによるパターンマッチング構文でも、case-whenと同じようにthenが使える (場合によっては使う必要がある)。
thenとは使われることは稀だが、Ruby では
-thenがキーワードになっている。次のように使う:if cond then - puts "Y" -else - puts "N" -end -このキーワードが現れうる場所はいくつかあり、
if、unless、rescue、case構文がそれに当たる。 +if cond then + puts "Y" +else + puts "N" +end +このキーワードが現れうる場所はいくつかあり、
-if、unless、rescue、case構文がそれに当たる。 上記のように、何か条件を書いた後thenを置き、式がそこで終了していることを示すマーカーとして機能する。# Example: - -if x then - a -end - -unless x then - a -end - -begin - a -rescue then - b -end - -case x -when p then - a -end -なぜ普段は書かなくてもよいのか
+# Example: + +if x then + a +end + +unless x then + a +end + +begin + a +rescue then + b +end + +case x +when p then + a +end +なぜ普段は書かなくてもよいのか
普通 Ruby のコードで
-thenを書くことはない。なぜか。次のコードを実行してみるとわかる。if true puts 'Hello, World!' end -次のような構文エラーが出力される。
-20:1: syntax error, unexpected local variable or method, expecting `then' or ';' or '\n' -if true puts 'Hello, World!' end +if true puts 'Hello, World!' end +次のような構文エラーが出力される。
+20:1: syntax error, unexpected local variable or method, expecting `then' or ';' or '\n' +if true puts 'Hello, World!' end ^~~~ -20:1: syntax error, unexpected `end', expecting end-of-input -...f true puts 'Hello, World!' end +20:1: syntax error, unexpected `end', expecting end-of-input +...f true puts 'Hello, World!' end二つ目のメッセージは無視して一つ目を読むと、
thenか;か改行が来るはずのところ変数だかメソッドだかが現れたことによりエラーとなっているようだ。ポイントは改行が
-then(や;) の代わりとなることである。trueの後に改行を入れてみる。if true -puts 'Hello, World!' end -無事 Hello, World! と出力されるようになった。
+if true +puts 'Hello, World!' end +無事 Hello, World! と出力されるようになった。
なぜ
thenや;や改行が必要かなぜ
-thenや;や改行 (以下 「then等」) が必要なのだろうか。次の例を見てほしい:if a b end -
thenも;も改行もないのでエラーになるが、これは条件式がどこまで続いているのかわからないためだ。 +if a b end +-
thenも;も改行もないのでエラーになるが、これは条件式がどこまで続いているのかわからないためだ。 この例は二通りに解釈できる。# a という変数かメソッドの評価結果が truthy なら b という変数かメソッドを評価 -if a then - b -end -# a というメソッドに b という変数かメソッドの評価結果を渡して呼び出し、 -# その結果が truthy なら何もしない -if a(b) then -end -
then等はこの曖昧性を排除するためにあり、条件式はifからthen等までの間にある、ということを明確にする。 +# a という変数かメソッドの評価結果が truthy なら b という変数かメソッドを評価 +if a then + b +end +# a というメソッドに b という変数かメソッドの評価結果を渡して呼び出し、 +# その結果が truthy なら何もしない +if a(b) then +end +
then等はこの曖昧性を排除するためにあり、条件式はifからthen等までの間にある、ということを明確にする。 C系のif後に来る(/)や、Python の:、Rust/Go/Swift などの{も同じ役割を持つ。Ruby の場合、プログラマーが書きやすいよう改行でもって
thenが代用できるので、ほとんどの場合thenは必要ない。@@ -112,33 +112,33 @@ C系の
case-inにおけるthenif後に来る(/)や、Python ;ここで、
keyword_inは文字通りin、p_top_exprはいわゆるパターン、thenはthenキーワードのことではなく、この記事でthen等と呼んでいるもの、つまりthenキーワード、;、改行のいずれかである。これにより、
-case-whenによる従来の構文と同じように、then等をパターンの後ろに挿入すればよいことがわかった。つまり次の3通りのいずれかになる:case x -in 1 then a -in 2 then b -in 3 then c -end - -case x -in 1 - a -in 2 - b -in 3 - c -end - -case x -in 1; a -in 2; b -in 3; c -end -ところで、
-p_top_exprにはifによる guard clause が書けるので、その場合はif-thenと似たような見た目になる。case x -in 0 then a -in n if n < 0 then b -in n then c -end -まとめ
+case x +in 1 then a +in 2 then b +in 3 then c +end + +case x +in 1 + a +in 2 + b +in 3 + c +end + +case x +in 1; a +in 2; b +in 3; c +end +ところで、
+p_top_exprにはifによる guard clause が書けるので、その場合はif-thenと似たような見た目になる。case x +in 0 then a +in n if n < 0 then b +in n then c +end +まとめ
ifやcaseの条件の後ろにはthen、;、改行のいずれかが必要@@ -164,13 +164,13 @@ C系の
if後に来る(/)や、Python
Objectクラスに定義されているRUBY_ENGINEという定数がこの用途に使える。上記ページの例から引用する:
-$ ruby-1.9.1 -ve 'p RUBY_ENGINE' -ruby 1.9.1p0 (2009-03-04 revision 22762) [x86_64-linux] -"ruby" -$ jruby -ve 'p RUBY_ENGINE' -jruby 1.2.0 (ruby 1.8.6 patchlevel 287) (2009-03-16 rev 9419) [i386-java] -"jruby" -それぞれの処理系がどのような値を返すかだが、stack overflow に良い質問と回答があった。
+$ ruby-1.9.1 -ve 'p RUBY_ENGINE' +ruby 1.9.1p0 (2009-03-04 revision 22762) [x86_64-linux] +"ruby" +$ jruby -ve 'p RUBY_ENGINE' +jruby 1.2.0 (ruby 1.8.6 patchlevel 287) (2009-03-16 rev 9419) [i386-java] +"jruby" +それぞれの処理系がどのような値を返すかだが、stack overflow に良い質問と回答があった。
What values for RUBY_ENGINE correspond to which Ruby implementations? より引用:
@@ -219,11 +219,11 @@ jruby 1.2.0 (ruby 1.8.6 patchlevel 287) (2009-03-16 rev 9419) [i386-java]
なお、この質問・回答は 2014年になされたものであり、値は変わっている可能性がある。MRI (aka CRuby) については執筆時現在 (2020/12/8) も
'ruby'が返ってくることを確認済み。この表にない主要な処理系として、mruby は
'mruby'を返す。mruby 該当部分のソース より引用:
-]]> +/* - * Ruby engine. - */ -#define MRUBY_RUBY_ENGINE "mruby" -]]> diff --git a/docs/tags/ruby/index.html b/docs/tags/ruby/index.html index c20b848..a23cbbf 100644 --- a/docs/tags/ruby/index.html +++ b/docs/tags/ruby/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/tags/ruby/page/1/index.html b/docs/tags/ruby/page/1/index.html index 62d3407..9374bc8 100644 --- a/docs/tags/ruby/page/1/index.html +++ b/docs/tags/ruby/page/1/index.html @@ -1 +1,10 @@ -/* + * Ruby engine. + */ +#define MRUBY_RUBY_ENGINE "mruby" +https://blog.nsfisis.dev/tags/ruby/ \ No newline at end of file + + + +https://blog.nsfisis.dev/tags/ruby/ + + + + + + diff --git a/docs/tags/ruby3/feed.xml b/docs/tags/ruby3/feed.xml index 066258a..530cc62 100644 --- a/docs/tags/ruby3/feed.xml +++ b/docs/tags/ruby3/feed.xml @@ -20,61 +20,61 @@
case-inによるパターンマッチング構文でも、case-whenと同じようにthenが使える (場合によっては使う必要がある)。
thenとは使われることは稀だが、Ruby では
-thenがキーワードになっている。次のように使う:if cond then - puts "Y" -else - puts "N" -end -このキーワードが現れうる場所はいくつかあり、
if、unless、rescue、case構文がそれに当たる。 +if cond then + puts "Y" +else + puts "N" +end +このキーワードが現れうる場所はいくつかあり、
-if、unless、rescue、case構文がそれに当たる。 上記のように、何か条件を書いた後thenを置き、式がそこで終了していることを示すマーカーとして機能する。# Example: - -if x then - a -end - -unless x then - a -end - -begin - a -rescue then - b -end - -case x -when p then - a -end -なぜ普段は書かなくてもよいのか
+# Example: + +if x then + a +end + +unless x then + a +end + +begin + a +rescue then + b +end + +case x +when p then + a +end +なぜ普段は書かなくてもよいのか
普通 Ruby のコードで
-thenを書くことはない。なぜか。次のコードを実行してみるとわかる。if true puts 'Hello, World!' end -次のような構文エラーが出力される。
-20:1: syntax error, unexpected local variable or method, expecting `then' or ';' or '\n' -if true puts 'Hello, World!' end +if true puts 'Hello, World!' end +次のような構文エラーが出力される。
+20:1: syntax error, unexpected local variable or method, expecting `then' or ';' or '\n' +if true puts 'Hello, World!' end ^~~~ -20:1: syntax error, unexpected `end', expecting end-of-input -...f true puts 'Hello, World!' end +20:1: syntax error, unexpected `end', expecting end-of-input +...f true puts 'Hello, World!' end二つ目のメッセージは無視して一つ目を読むと、
thenか;か改行が来るはずのところ変数だかメソッドだかが現れたことによりエラーとなっているようだ。ポイントは改行が
-then(や;) の代わりとなることである。trueの後に改行を入れてみる。if true -puts 'Hello, World!' end -無事 Hello, World! と出力されるようになった。
+if true +puts 'Hello, World!' end +無事 Hello, World! と出力されるようになった。
なぜ
thenや;や改行が必要かなぜ
-thenや;や改行 (以下 「then等」) が必要なのだろうか。次の例を見てほしい:if a b end -
thenも;も改行もないのでエラーになるが、これは条件式がどこまで続いているのかわからないためだ。 +if a b end +-
thenも;も改行もないのでエラーになるが、これは条件式がどこまで続いているのかわからないためだ。 この例は二通りに解釈できる。# a という変数かメソッドの評価結果が truthy なら b という変数かメソッドを評価 -if a then - b -end -# a というメソッドに b という変数かメソッドの評価結果を渡して呼び出し、 -# その結果が truthy なら何もしない -if a(b) then -end -
then等はこの曖昧性を排除するためにあり、条件式はifからthen等までの間にある、ということを明確にする。 +# a という変数かメソッドの評価結果が truthy なら b という変数かメソッドを評価 +if a then + b +end +# a というメソッドに b という変数かメソッドの評価結果を渡して呼び出し、 +# その結果が truthy なら何もしない +if a(b) then +end +
then等はこの曖昧性を排除するためにあり、条件式はifからthen等までの間にある、ということを明確にする。 C系のif後に来る(/)や、Python の:、Rust/Go/Swift などの{も同じ役割を持つ。Ruby の場合、プログラマーが書きやすいよう改行でもって
thenが代用できるので、ほとんどの場合thenは必要ない。@@ -112,33 +112,33 @@ C系の
case-inにおけるthenif後に来る(/)や、Python ;ここで、
keyword_inは文字通りin、p_top_exprはいわゆるパターン、thenはthenキーワードのことではなく、この記事でthen等と呼んでいるもの、つまりthenキーワード、;、改行のいずれかである。これにより、
-case-whenによる従来の構文と同じように、then等をパターンの後ろに挿入すればよいことがわかった。つまり次の3通りのいずれかになる:case x -in 1 then a -in 2 then b -in 3 then c -end - -case x -in 1 - a -in 2 - b -in 3 - c -end - -case x -in 1; a -in 2; b -in 3; c -end -ところで、
-p_top_exprにはifによる guard clause が書けるので、その場合はif-thenと似たような見た目になる。case x -in 0 then a -in n if n < 0 then b -in n then c -end -まとめ
+case x +in 1 then a +in 2 then b +in 3 then c +end + +case x +in 1 + a +in 2 + b +in 3 + c +end + +case x +in 1; a +in 2; b +in 3; c +end +ところで、
+p_top_exprにはifによる guard clause が書けるので、その場合はif-thenと似たような見た目になる。case x +in 0 then a +in n if n < 0 then b +in n then c +end +まとめ
ifやcaseの条件の後ろにはthen、;、改行のいずれかが必要diff --git a/docs/tags/ruby3/index.html b/docs/tags/ruby3/index.html index 9f2b111..b204a64 100644 --- a/docs/tags/ruby3/index.html +++ b/docs/tags/ruby3/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/tags/ruby3/page/1/index.html b/docs/tags/ruby3/page/1/index.html index 5db85b3..15caf8a 100644 --- a/docs/tags/ruby3/page/1/index.html +++ b/docs/tags/ruby3/page/1/index.html @@ -1 +1,10 @@ -
https://blog.nsfisis.dev/tags/ruby3/ \ No newline at end of file + + + +https://blog.nsfisis.dev/tags/ruby3/ + + + + + + diff --git a/docs/tags/rust/feed.xml b/docs/tags/rust/feed.xml index 6b5c112..60f98d3 100644 --- a/docs/tags/rust/feed.xml +++ b/docs/tags/rust/feed.xml @@ -18,27 +18,27 @@
前置き
Rust において、プリミティブ型の名前は予約語でない。したがって、次のコードは合法である。
-#![allow(non_camel_case_types)] -#![allow(dead_code)] - -struct bool; -struct char; -struct i8; -struct i16; -struct i32; -struct i64; -struct i128; -struct isize; -struct u8; -struct u16; -struct u32; -struct u64; -struct u128; -struct usize; -struct f32; -struct f64; -struct str; -では、普段単に
+boolと書いたとき、このboolは一体どこから来ているのか。rustc のソースを追ってみた。#![allow(non_camel_case_types)] +#![allow(dead_code)] + +struct bool; +struct char; +struct i8; +struct i16; +struct i32; +struct i64; +struct i128; +struct isize; +struct u8; +struct u16; +struct u32; +struct u64; +struct u128; +struct usize; +struct f32; +struct f64; +struct str; +では、普段単に
boolと書いたとき、このboolは一体どこから来ているのか。rustc のソースを追ってみた。@@ -49,91 +49,91 @@前提知識: 一般的なコンパイラの構造、用語。
rustcそのものの知識は不要 (というよりも筆者自身がよく知らない)大雑把な構造としては、
compilerフォルダ以下にrustc_*という名前のクレートが数十個入っている。これがどうやらrustcコマンドの実装部のようだ。-
rustcはセルフホストされている (=rustc自身が Rust で書かれている) ので、boolやcharなどで適当に検索をかけてもノイズが多すぎて話にならない。 しかし、お誂え向きなことにi128/u128というコンパイラ自身が使うことがなさそうな型が存在するのでこれを使ってgit grepしてみる。$ git grep "\bi128\b" | wc # i128 +$ git grep "\bi128\b" | wc # i128 165 1069 15790 -$ git grep "\bu128\b" | wc # u128 +$ git grep "\bu128\b" | wc # u128 293 2127 26667 -$ git grep "\bbool\b" | wc # cf. bool の結果 +$ git grep "\bbool\b" | wc # cf. bool の結果 3563 23577 294659165 程度であれば探すことができそうだ。今回は、クレート名を見ておおよその当たりをつけた。
-$ git grep "\bi128\b" +$ git grep "\bi128\b" ... rustc_resolve/src/lib.rs: table.insert(sym::i128, Int(IntTy::I128)); ...-
rustc_resolveというのはいかにも名前解決を担いそうなクレート名である。該当箇所を見てみる。/// Interns the names of the primitive types. -/// -/// All other types are defined somewhere and possibly imported, but the primitive ones need -/// special handling, since they have no place of origin. -struct PrimitiveTypeTable { - primitive_types: FxHashMap<Symbol, PrimTy>, -} - -impl PrimitiveTypeTable { - fn new() -> PrimitiveTypeTable { - let mut table = FxHashMap::default(); - - table.insert(sym::bool, Bool); - table.insert(sym::char, Char); - table.insert(sym::f32, Float(FloatTy::F32)); - table.insert(sym::f64, Float(FloatTy::F64)); - table.insert(sym::isize, Int(IntTy::Isize)); - table.insert(sym::i8, Int(IntTy::I8)); - table.insert(sym::i16, Int(IntTy::I16)); - table.insert(sym::i32, Int(IntTy::I32)); - table.insert(sym::i64, Int(IntTy::I64)); - table.insert(sym::i128, Int(IntTy::I128)); - table.insert(sym::str, Str); - table.insert(sym::usize, Uint(UintTy::Usize)); - table.insert(sym::u8, Uint(UintTy::U8)); - table.insert(sym::u16, Uint(UintTy::U16)); - table.insert(sym::u32, Uint(UintTy::U32)); - table.insert(sym::u64, Uint(UintTy::U64)); - table.insert(sym::u128, Uint(UintTy::U128)); - Self { primitive_types: table } - } -} -これは初めに列挙したプリミティブ型の一覧と一致している。doc comment にも、
+/// Interns the names of the primitive types. +/// +/// All other types are defined somewhere and possibly imported, but the primitive ones need +/// special handling, since they have no place of origin. +struct PrimitiveTypeTable { + primitive_types: FxHashMap<Symbol, PrimTy>, +} + +impl PrimitiveTypeTable { + fn new() -> PrimitiveTypeTable { + let mut table = FxHashMap::default(); + + table.insert(sym::bool, Bool); + table.insert(sym::char, Char); + table.insert(sym::f32, Float(FloatTy::F32)); + table.insert(sym::f64, Float(FloatTy::F64)); + table.insert(sym::isize, Int(IntTy::Isize)); + table.insert(sym::i8, Int(IntTy::I8)); + table.insert(sym::i16, Int(IntTy::I16)); + table.insert(sym::i32, Int(IntTy::I32)); + table.insert(sym::i64, Int(IntTy::I64)); + table.insert(sym::i128, Int(IntTy::I128)); + table.insert(sym::str, Str); + table.insert(sym::usize, Uint(UintTy::Usize)); + table.insert(sym::u8, Uint(UintTy::U8)); + table.insert(sym::u16, Uint(UintTy::U16)); + table.insert(sym::u32, Uint(UintTy::U32)); + table.insert(sym::u64, Uint(UintTy::U64)); + table.insert(sym::u128, Uint(UintTy::U128)); + Self { primitive_types: table } + } +} +これは初めに列挙したプリミティブ型の一覧と一致している。doc comment にも、
All other types are defined somewhere and possibly imported, but the primitive ones need special handling, since they have no place of origin.
とある。次はこの struct の使用箇所を追う。追うと言っても使われている箇所は次の一箇所しかない。なお説明に不要な箇所は大きく削っている。
-/// This resolves the identifier `ident` in the namespace `ns` in the current lexical scope. - /// (略) - fn resolve_ident_in_lexical_scope( - &mut self, - mut ident: Ident, - ns: Namespace, - // (略) - ) -> Option<LexicalScopeBinding<'a>> { - // (略) - - if ns == TypeNS { - if let Some(prim_ty) = self.primitive_type_table.primitive_types.get(&ident.name) { - let binding = - (Res::PrimTy(*prim_ty), ty::Visibility::Public, DUMMY_SP, ExpnId::root()) - .to_name_binding(self.arenas); - return Some(LexicalScopeBinding::Item(binding)); - } - } - - None - } -関数名や doc comment が示している通り、この関数は識別子 (identifier, ident) を現在のレキシカルスコープ内で解決 (resolve) する。 +
/// This resolves the identifier `ident` in the namespace `ns` in the current lexical scope. + /// (略) + fn resolve_ident_in_lexical_scope( + &mut self, + mut ident: Ident, + ns: Namespace, + // (略) + ) -> Option<LexicalScopeBinding<'a>> { + // (略) + + if ns == TypeNS { + if let Some(prim_ty) = self.primitive_type_table.primitive_types.get(&ident.name) { + let binding = + (Res::PrimTy(*prim_ty), ty::Visibility::Public, DUMMY_SP, ExpnId::root()) + .to_name_binding(self.arenas); + return Some(LexicalScopeBinding::Item(binding)); + } + } + + None + } +関数名や doc comment が示している通り、この関数は識別子 (identifier, ident) を現在のレキシカルスコープ内で解決 (resolve) する。
if ns == TypeNSのブロック内では、primitive_type_table(上記のPrimitiveTypeTable::new()で作られた変数) に含まれている識別子 (bool、i32など) かどうか判定し、そうであればそれに紐づけられたプリミティブ型を返している。なお、
nsは「名前空間」を示す変数である。Rust における名前空間はC言語におけるそれとほとんど同じで、今探している名前が関数名/変数名なのか型なのかマクロなのかを区別している。このifは、プリミティブ型に解決されるのは型を探しているときだけだ、と言っている。重要なのは、これが
resolve_ident_in_lexical_scope()の最後に書かれている点である。つまり、最初に挙げたプリミティブ型の識別子は、「名前解決の最終段階で」、「他に同名の型が見つかっていなければ」プリミティブ型として解決される。動作がわかったところで、例として次のコードを考える。
-#![allow(non_camel_case_types)] - -struct bool; - -fn main() { - let _: bool = bool; -} -ここで
+main()のboolはstruct boolとして解決される。なぜなら、プリミティブ型の判定をする前にboolという名前の別の型が見つかるからだ。#![allow(non_camel_case_types)] + +struct bool; + +fn main() { + let _: bool = bool; +} +ここで
main()のboolはstruct boolとして解決される。なぜなら、プリミティブ型の判定をする前にboolという名前の別の型が見つかるからだ。まとめ
Rust のプリミティブ型は予約語ではない。名前解決の最終段階で特別扱いされ、他に同名の型が見つかっていなければ対応するプリミティブ型に解決される。
]]> diff --git a/docs/tags/rust/index.html b/docs/tags/rust/index.html index 2381508..7b89cc4 100644 --- a/docs/tags/rust/index.html +++ b/docs/tags/rust/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/tags/rust/page/1/index.html b/docs/tags/rust/page/1/index.html index d041e49..5cf7d43 100644 --- a/docs/tags/rust/page/1/index.html +++ b/docs/tags/rust/page/1/index.html @@ -1 +1,10 @@ -https://blog.nsfisis.dev/tags/rust/ \ No newline at end of file + + + +https://blog.nsfisis.dev/tags/rust/ + + + + + + diff --git a/docs/tags/vim/feed.xml b/docs/tags/vim/feed.xml index 42c6110..73bb677 100644 --- a/docs/tags/vim/feed.xml +++ b/docs/tags/vim/feed.xml @@ -43,10 +43,10 @@ Huge version without GUI.
:g/^/m0のように組み合わせると、「すべての行を1行ずつ 0行目(1行目の上)に動かす」という動きをする。これは確かに行の入れ替えになっている。なお、
-:g/^/m0は全ての行を入れ替えるが、:N,Mg/^/mN-1とすることで N行目から M行目を処理範囲とするよう拡張できる。手でこれを入力するわけにはいかないので、次のようなコマンドを用意する。command! -bar -range=% - \ Reverse - \ <line1>,<line2>g/^/m<line1>-1 -これは望みの動作をするが、実際に実行してみると全行がハイライトされてしまう。次節で詳細を述べる。
+command! -bar -range=% + \ Reverse + \ <line1>,<line2>g/^/m<line1>-1 +これは望みの動作をするが、実際に実行してみると全行がハイライトされてしまう。次節で詳細を述べる。
:g/^/m0の問題点
:globalコマンドは各行に対してマッチングを行う際、現在の検索パターンを上書きしてしまう。^は行の先頭にマッチするため、結果として全ての行がハイライトされてしまう。'hlsearch'オプションを無効にしている場合その限りではないが、その場合でも直前の検索パターンが失われてしまうとnコマンドなどの際に不便である。@@ -58,14 +58,14 @@ Huge version without GUI. より簡潔な方法を見つけたので次節に追記した前述した
-:Reverseコマンドの定義を少し変えて、次のようにする:function! s:reverse_lines(from, to) abort - execute printf("%d,%dg/^/m%d", a:from, a:to, a:from - 1) -endfunction - -command! -bar -range=% - \ Reverse - \ call <SID>reverse_lines(<line1>, <line2>) -実行しているコマンドが変わったわけではないが、関数呼び出しを経由するようにした。これだけで前述の問題が解決する。
+function! s:reverse_lines(from, to) abort + execute printf("%d,%dg/^/m%d", a:from, a:to, a:from - 1) +endfunction + +command! -bar -range=% + \ Reverse + \ call <SID>reverse_lines(<line1>, <line2>) +実行しているコマンドが変わったわけではないが、関数呼び出しを経由するようにした。これだけで前述の問題が解決する。
この理由は、ユーザー定義関数を実行する際は検索パターンが一度保存され、実行が終了したあと復元されるため。結果として検索パターンが
^で上書きされることがなくなる。Vim のヘルプから該当箇所を引用する (強調は筆者による)。
@@ -89,20 +89,20 @@ executing autocommands |autocmd-searchpat|.-[2020/9/28追記] より簡潔な方法を見つけたため追記する
command! -bar -range=% - \ Reverse - \ keeppatterns <line1>,<line2>g/^/m<line1>-1 -まさにこのための Exコマンド、
+:keeppatternsが存在する。:keeppatterns {command}のように使い、読んで字の如く、後ろに続く Exコマンドを「現在の検索パターンを保ったまま」実行する。はるかに分かりやすく意図を表現できる。command! -bar -range=% + \ Reverse + \ keeppatterns <line1>,<line2>g/^/m<line1>-1 +まさにこのための Exコマンド、
:keeppatternsが存在する。:keeppatterns {command}のように使い、読んで字の如く、後ろに続く Exコマンドを「現在の検索パターンを保ったまま」実行する。はるかに分かりやすく意図を表現できる。:h :keeppatterns
コピペ用再掲
-]]> +" License: Public Domain - -command! -bar -range=% - \ Reverse - \ keeppatterns <line1>,<line2>g/^/m<line1>-1 -]]>" License: Public Domain + +command! -bar -range=% + \ Reverse + \ keeppatterns <line1>,<line2>g/^/m<line1>-1 +- @@ -136,26 +136,26 @@ executing autocommands |autocmd-searchpat|.
vim のソースコード
以下は、autocmd events の名前と内部で使われている整数値とのマッピングを定義している箇所である。見ての通り、上でエイリアスではないかと述べた3組には、それぞれ同じ内部値が使われている。
https://github.com/vim/vim/blob/8e6be34338f13a6a625f19bcef82019c9adc65f2/src/autocmd.c#L85-L86
-{"BufAdd", EVENT_BUFADD}, - {"BufCreate", EVENT_BUFADD}, -https://github.com/vim/vim/blob/8e6be34338f13a6a625f19bcef82019c9adc65f2/src/autocmd.c#L95-L97
-{"BufRead", EVENT_BUFREADPOST}, - {"BufReadCmd", EVENT_BUFREADCMD}, - {"BufReadPost", EVENT_BUFREADPOST}, -https://github.com/vim/vim/blob/8e6be34338f13a6a625f19bcef82019c9adc65f2/src/autocmd.c#L103-L105
-{"BufWrite", EVENT_BUFWRITEPRE}, - {"BufWritePost", EVENT_BUFWRITEPOST}, - {"BufWritePre", EVENT_BUFWRITEPRE}, -neovim のソースコード
+{"BufAdd", EVENT_BUFADD}, + {"BufCreate", EVENT_BUFADD}, +https://github.com/vim/vim/blob/8e6be34338f13a6a625f19bcef82019c9adc65f2/src/autocmd.c#L95-L97
+{"BufRead", EVENT_BUFREADPOST}, + {"BufReadCmd", EVENT_BUFREADCMD}, + {"BufReadPost", EVENT_BUFREADPOST}, +https://github.com/vim/vim/blob/8e6be34338f13a6a625f19bcef82019c9adc65f2/src/autocmd.c#L103-L105
+{"BufWrite", EVENT_BUFWRITEPRE}, + {"BufWritePost", EVENT_BUFWRITEPOST}, + {"BufWritePre", EVENT_BUFWRITEPRE}, +neovim のソースコード
neovim の場合でも同様のマッピングが定義されているが、こちらの場合は Lua で書かれている。以下にある通り、はっきり
-aliasesと書かれている。aliases = { - BufCreate = 'BufAdd', - BufRead = 'BufReadPost', - BufWrite = 'BufWritePre', - FileEncoding = 'EncodingChanged', - }, -ところで、上では取り上げなかった
+FileEncodingだが、これは:help FileEncodingにしっかりと書いてある。aliases = { + BufCreate = 'BufAdd', + BufRead = 'BufReadPost', + BufWrite = 'BufWritePre', + FileEncoding = 'EncodingChanged', + }, +ところで、上では取り上げなかった
FileEncodingだが、これは:help FileEncodingにしっかりと書いてある。*FileEncoding* FileEncoding Obsolete. It still works and is equivalent to |EncodingChanged|. diff --git a/docs/tags/vim/index.html b/docs/tags/vim/index.html index dfdca95..5964ab8 100644 --- a/docs/tags/vim/index.html +++ b/docs/tags/vim/index.html @@ -15,7 +15,7 @@ - + diff --git a/docs/tags/vim/page/1/index.html b/docs/tags/vim/page/1/index.html index 3ac6e63..52be41d 100644 --- a/docs/tags/vim/page/1/index.html +++ b/docs/tags/vim/page/1/index.html @@ -1 +1,10 @@ -https://blog.nsfisis.dev/tags/vim/ \ No newline at end of file + + + +https://blog.nsfisis.dev/tags/vim/ + + + + + + -- cgit v1.2.3-70-g09d2