From 4681e861efcdf9b0b73e3803e70712bc55162863 Mon Sep 17 00:00:00 2001 From: nsfisis Date: Sat, 8 Aug 2026 10:38:18 +0900 Subject: feat(slides): improve slide controller --- .../index.html | 753 +++++++++++---------- 1 file changed, 385 insertions(+), 368 deletions(-) (limited to 'services/nuldoc/public/blog/posts/2022-09-29/write-fizzbuzz-in-php-2-letters-per-line/index.html') diff --git a/services/nuldoc/public/blog/posts/2022-09-29/write-fizzbuzz-in-php-2-letters-per-line/index.html b/services/nuldoc/public/blog/posts/2022-09-29/write-fizzbuzz-in-php-2-letters-per-line/index.html index cba40bc8..b49b7096 100644 --- a/services/nuldoc/public/blog/posts/2022-09-29/write-fizzbuzz-in-php-2-letters-per-line/index.html +++ b/services/nuldoc/public/blog/posts/2022-09-29/write-fizzbuzz-in-php-2-letters-per-line/index.html @@ -15,7 +15,7 @@ 【PHP】 fizzbuzz を書く。1行あたり2文字で。|REPL: Rest-Eat-Program Loop - +
@@ -158,80 +158,81 @@ 特に、C言語でこのような試みをおこなったことがあるかたならそう思うだろう。事実、Cでのこの制約はほとんど無意味に等しい。

-
#\ -
i\ -
n\ -
c\ -
l\ -
u\ -
d\ -
e\ -
<\ -
s\ -
t\ -
d\ -
i\ -
o\ -
.\ -
h\ -
>\ -
/* -
*/ -
i\ -
n\ -
t\ -
/* -
*/ -
m\ -
a\ -
i\ -
n( -
){ -
f\ -
o\ -
r( -
i\ -
n\ -
t\ -
/* -
*/ -
i= -
1; -
i< -
1\ -
0\ -
0; -
i\ -
+\ -
+) -
if -
(i -
%\ -
15 -
== -
0) -
p\ -
r\ -
i\ -
n\ -
t\ -
f( -
"\ -
F\ -
i\ -
z\ -
z\ -
B\ -
u\ -
z\ -
z\ -
%\ -
c\ -
", -
10 -
); -
-
/* あとは同じように普通のプログラムを変形するだけなので省略 */
+
#\
+i\
+n\
+c\
+l\
+u\
+d\
+e\
+<\
+s\
+t\
+d\
+i\
+o\
+.\
+h\
+>\
+/*
+*/
+i\
+n\
+t\
+/*
+*/
+m\
+a\
+i\
+n(
+){
+f\
+o\
+r(
+i\
+n\
+t\
+/*
+*/
+i=
+1;
+i<
+1\
+0\
+0;
+i\
++\
++)
+if
+(i
+%\
+15
+==
+0)
+p\
+r\
+i\
+n\
+t\
+f(
+"\
+F\
+i\
+z\
+z\
+B\
+u\
+z\
+z\
+%\
+c\
+",
+10
+);
+
+/* あとは同じように普通のプログラムを変形するだけなので省略 */
+

バックスラッシュを使った行継続がトークンを区切らない、というのがポイントだ。 @@ -273,10 +274,11 @@ また、2文字だと文字列がまともに書けないのも辛い。'' だけで2文字使うので、「1文字の文字列リテラル」というものを書くことができない。PHP では文字列リテラル中に生の改行が書けるので

-
$a -
=' -
a' -
;;
+
$a
+='
+a'
+;;
+

とすると $a"\na" になるのだが、余計な改行が入ってしまう。 @@ -293,11 +295,12 @@ まずは普通に書くとしよう。

-
<?php -
-
for ($i = 1; $i < 100; $i++) { -
echo (($i % 3 ? '' : 'Fizz') . ($i % 5 ? '' : 'Buzz') ?: $i) . "\n"; -
}
+
<?php
+
+for ($i = 1; $i < 100; $i++) {
+  echo (($i % 3 ? '' : 'Fizz') . ($i % 5 ? '' : 'Buzz') ?: $i) . "\n";
+}
+

素直に書いた fizzbuzz とは言い難いが、このくらいは普通だということにしておかないと、この先がやっていられないので許してほしい。 @@ -309,14 +312,15 @@ for は、3文字もある長いキーワードである。こんなものは使えない。array_ 系の関数を使って、適当に置き換えるとしよう。

-
<?php -
-
$s = range(1, 100); -
array_walk( -
$s, -
fn($i) => -
printf((($i % 3 ? '' : 'Fizz') . ($i % 5 ? '' : 'Buzz') ?: $i) . "\n"), -
);
+
<?php
+
+$s = range(1, 100);
+array_walk(
+$s,
+fn($i) =>
+printf((($i % 3 ? '' : 'Fizz') . ($i % 5 ? '' : 'Buzz') ?: $i) . "\n"),
+);
+

array_walkrangeprintf といった for よりも長いトークンが現れてしまったが、これは次節で直すことにする。なお、echo は文 (statement) であり式 (expression) ではないので、式である printf に置き換えた。 @@ -328,18 +332,19 @@ rangearray_walkprintf は長すぎるのでどうにかせねばならない。ここで、PHP の可変関数を使う。可変関数とは、関数名が文字列として入った変数を経由して、関数を呼び出す機能である。

-
<?php -
-
$r = 'range'; -
$w = 'array_walk'; -
$p = 'printf'; -
-
$s = $r(1, 100); -
$w( -
$s, -
fn($i) => -
$p((($i % 3 ? '' : 'Fizz') . ($i % 5 ? '' : 'Buzz') ?: $i) . "\n"), -
);
+
<?php
+
+$r = 'range';
+$w = 'array_walk';
+$p = 'printf';
+
+$s = $r(1, 100);
+$w(
+$s,
+fn($i) =>
+$p((($i % 3 ? '' : 'Fizz') . ($i % 5 ? '' : 'Buzz') ?: $i) . "\n"),
+);
+

これで関数を呼び出している所は短くなった。では、$r$w$p、また 'Fizz''Buzz' はどうやって 1 行 2 文字に収めるのか。次のテクニックへ移ろう。 @@ -361,26 +366,28 @@ というルールがない場合、「未定義の定数が評価された場合、その定数の名前が値になる」という PHP 7.x までの仕様が利用できる。例えば、 Fizz という文字列が欲しければ、次のようにする。

-
$f -
=F -
.i -
.z -
.z -
;;
+
$f
+=F
+.i
+.z
+.z
+;;
+

こうして簡単に文字列を作れる。なお、この仕様は 7.x 時点でも警告を受けるので、@ 演算子を使って抑制してやるとよい。

-
$f -
=@ -
F. -
@i -
.# -
@z -
.# -
@z -
;;
+
$f
+=@
+F.
+@i
+.#
+@z
+.#
+@z
+;;
+

むしろ、このことがわかっていたからこそ PHP 8.x での動作を要件に課したところがある。 @@ -395,66 +402,70 @@ ずばり、文字列同士のビット演算を使う。PHP では、文字列同士でビット演算 (&|^) をした場合、文字列の各バイトごとに指定したビット演算がなされ、それを結合したものが演算結果となる。

-
$a = "12345"; -
$b = "world"; -
-
// $a ^ $b は次のコードと同じ -
$result = ''; -
for ($i = 0; $i < min(strlen($a), strlen($b)); $i++) { -
$result .= $a[$i] ^ $b[$i]; -
} -
-
echo $result; -
// => F]AXQ
+
$a = "12345";
+$b = "world";
+
+// $a ^ $b は次のコードと同じ
+$result = '';
+for ($i = 0; $i < min(strlen($a), strlen($b)); $i++) {
+$result .= $a[$i] ^ $b[$i];
+}
+
+echo $result;
+// => F]AXQ
+

これを踏まえ、次のコードを見てみよう。

-
$x = "x\nOm\n"; -
$y = "\nk!\no"; -
$r = $x ^ $y; -
echo "$r\n";
+
$x = "x\nOm\n";
+$y = "\nk!\no";
+$r = $x ^ $y;
+echo "$r\n";
+

実行すると、range が表示される。さて、PHP では文字列リテラル中に生の改行を直接書いてもよいのだった (「主な障害」の節を参照のこと)。書きかえてみよう。

-
$x -
='x -
Om -
'; -
$y -
=' -
k! -
o' -
; -
-
$r = $x ^ $y; -
echo "$r\n";
+
$x
+='x
+Om
+';
+$y
+='
+k!
+o'
+;
+
+$r = $x ^ $y;
+echo "$r\n";
+

さらに # を使って適当に調整すると、次のようになる。

-
$x -
=# -
'x -
Om -
'; -
$y -
=' -
k! -
o' -
;# -
$r -
=# -
$x -
^# -
$y -
;# -
-
echo "$r\n";
+
$x
+=#
+'x
+Om
+';
+$y
+='
+k!
+o'
+;#
+$r
+=#
+$x
+^#
+$y
+;#
+
+echo "$r\n";
+

1行あたり2文字で、range という文字列を生成することに成功した。他の必要な文字列にも、同様の処理をほどこす。 @@ -470,155 +481,156 @@ 完成したものがこちら。

-
<?php -
-
$x -
=# -
'i -
S' -
;; -
$y -
=' -
b! -
'; -
$c -
=# -
$x -
^# -
$y -
;# -
$x -
=# -
'x -
Om -
'; -
$y -
=' -
k! -
o' -
;# -
$r -
=# -
$x -
^# -
$y -
;# -
$x -
=# -
'k -
Sk -
~} -
Ma -
'; -
$y -
=' -
x! -
s! -
k! -
'; -
$w -
=# -
$x -
^# -
$y -
;# -
$x -
=# -
'z -
Hd -
G' -
;# -
$y -
=' -
x! -
~! -
'; -
$p -
=# -
$x -
^# -
$y -
;# -
$x -
=# -
'L -
[p -
'; -
$y -
=' -
c! -
'; -
$f -
=# -
$x -
^# -
$y -
;# -
$x -
=# -
'H -
[p -
'; -
$y -
=' -
_! -
'; -
$b -
=# -
$x -
^# -
$y -
;# -
$b -
[1 -
]= -
$c -
(# -
13 -
*9 -
); -
$s -
=# -
$r -
(1 -
,( -
10 -
** -
2) -
); -
$w -
(# -
$s -
,# -
fn -
(# -
$i -
)# -
=> -
$p -
(( -
(# -
$i -
%3 -
?# -
'' -
:# -
$f -
). -
(# -
$i -
%5 -
?# -
'' -
:# -
$b -
)? -
:# -
$i -
)# -
.' -
') -
);
+
<?php
+
+$x
+=#
+'i
+S'
+;;
+$y
+='
+b!
+';
+$c
+=#
+$x
+^#
+$y
+;#
+$x
+=#
+'x
+Om
+';
+$y
+='
+k!
+o'
+;#
+$r
+=#
+$x
+^#
+$y
+;#
+$x
+=#
+'k
+Sk
+~}
+Ma
+';
+$y
+='
+x!
+s!
+k!
+';
+$w
+=#
+$x
+^#
+$y
+;#
+$x
+=#
+'z
+Hd
+G'
+;#
+$y
+='
+x!
+~!
+';
+$p
+=#
+$x
+^#
+$y
+;#
+$x
+=#
+'L
+[p
+';
+$y
+='
+c!
+';
+$f
+=#
+$x
+^#
+$y
+;#
+$x
+=#
+'H
+[p
+';
+$y
+='
+_!
+';
+$b
+=#
+$x
+^#
+$y
+;#
+$b
+[1
+]=
+$c
+(#
+13
+*9
+);
+$s
+=#
+$r
+(1
+,(
+10
+**
+2)
+);
+$w
+(#
+$s
+,#
+fn
+(#
+$i
+)#
+=>
+$p
+((
+(#
+$i
+%3
+?#
+''
+:#
+$f
+).
+(#
+$i
+%5
+?#
+''
+:#
+$b
+)?
+:#
+$i
+)#
+.'
+')
+);
+
@@ -636,18 +648,19 @@ PHP では、バッククォートを使ってシェルを呼び出せる。これは shell_exec 関数と等価である。さて、PHP ではバックスラッシュによる行継続が使えないと書いたが、シェルでは使える (当然だが、呼び出されるシェルに依存する。Bash なら大丈夫だろう。知らんけど)。

-
<?php -
-
printf(` -
e\ -
c\ -
h\ -
o\ -
\ -
1\ -
2\ -
3\ -
`);
+
<?php
+
+printf(`
+e\
+c\
+h\
+o\
+\
+1\
+2\
+3\
+`);
+

なお、ここでは簡単のため出力に printf をそのまま使っているが、実際には printf という文字列を合成して可変関数で呼び出す。 @@ -669,56 +682,59 @@ もうこれ以上は不可能だと思っていたのだが、この記事の執筆中に解決する方法を思いついたので載せておく。

-
<?php -
-
$c = 'chr'; -
-
${ -
'_ -
'} -
=# -
$c -
(# -
32 -
). -
$c -
(# -
92 -
); -
-
printf(` -
e\ -
c\ -
h\ -
o\ -
${ -
'_ -
'} -
1\ -
2\ -
3\ -
`);
+
<?php
+
+$c = 'chr';
+
+${
+'_
+'}
+=#
+$c
+(#
+32
+).
+$c
+(#
+92
+);
+
+printf(`
+e\
+c\
+h\
+o\
+${
+'_
+'}
+1\
+2\
+3\
+`);
+

先程と同じく、chrprintf を生成する部分は長くなるので省いた。

-
${ -
'_ -
'}
+
${
+'_
+'}
+

は変数で、中にはスペースとエスケープが入っている (chr(32) . chr(92))。シェルに渡されている文字列は次のようになる。

-
e\ -
c\ -
h\ -
o\ -
\ -
1\ -
2\ -
3\
+
e\
+c\
+h\
+o\
+\
+1\
+2\
+3\
+

これは、前掲したコマンドと同じだ。かくして、スペースを陽に書かずにシェルをおおよそ自由に扱えるようになった。Fizzbuzz のワンライナーくらいすぐ書けるだろうから、あとはなんとかなるだろう (試してないけど)。 @@ -730,9 +746,10 @@ ちなみに、PHP 8.2 からは、この記法で Warning が出るようになるようだ。

-
${ -
'_ -
'}
+
${
+'_
+'}
+

最新版で警告が出るというのも美しくないので、私としては本編の解法を推す。 -- cgit v1.3.1