aboutsummaryrefslogtreecommitdiffhomepage
path: root/content/posts/2022-09-29/write-fizzbuzz-in-php-2-letters-per-line.xml
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2023-03-15 02:20:00 +0900
committernsfisis <nsfisis@gmail.com>2023-03-16 14:05:15 +0900
commit7b6319986030fc8a2fb5f851a431b5113e774f60 (patch)
tree522cb3837707660373ddaf43da26bc2bf1dbc683 /content/posts/2022-09-29/write-fizzbuzz-in-php-2-letters-per-line.xml
parentbfac7998b0f7abd8dbde016535037cf1d3174336 (diff)
downloadblog.nsfisis.dev-7b6319986030fc8a2fb5f851a431b5113e774f60.tar.gz
blog.nsfisis.dev-7b6319986030fc8a2fb5f851a431b5113e774f60.tar.zst
blog.nsfisis.dev-7b6319986030fc8a2fb5f851a431b5113e774f60.zip
fix(content): fix XML notations
Diffstat (limited to 'content/posts/2022-09-29/write-fizzbuzz-in-php-2-letters-per-line.xml')
-rw-r--r--content/posts/2022-09-29/write-fizzbuzz-in-php-2-letters-per-line.xml774
1 files changed, 421 insertions, 353 deletions
diff --git a/content/posts/2022-09-29/write-fizzbuzz-in-php-2-letters-per-line.xml b/content/posts/2022-09-29/write-fizzbuzz-in-php-2-letters-per-line.xml
index 9ef6654..5cb5d93 100644
--- a/content/posts/2022-09-29/write-fizzbuzz-in-php-2-letters-per-line.xml
+++ b/content/posts/2022-09-29/write-fizzbuzz-in-php-2-letters-per-line.xml
@@ -71,80 +71,84 @@
<title>主な障害</title>
<simpara>1行あたりの文字数など、適当に改行を挟めばいいだけではないのか?</simpara>
<simpara>特に、C言語でこのような試みをおこなったことがあるかたならそう思うだろう。事実、Cでのこの制約はほとんど無意味に等しい。</simpara>
- <programlisting language="c" linenumbering="unnumbered">#\
- i\
- n\
- c\
- l\
- u\
- d\
- e\
- &lt;\
- s\
- t\
- d\
- i\
- o\
- .\
- h\
- &gt;\
- /*
- */
- i\
- n\
- t\
- /*
- */
- m\
- a\
- i\
- n(
- ){
- f\
- o\
- r(
- i\
- n\
- t\
- /*
- */
- i=
- 1;
- i&lt;
- 1\
- 0\
- 0;
- i\
- +\
- +)
- if
- (i
- %\
- 15
- ==
- 0)
- p\
- r\
- i\
- n\
- t\
- f(
- "\
- F\
- i\
- z\
- z\
- B\
- u\
- z\
- z\
- %\
- c\
- ",
- 10
- );
+ <programlisting language="c" linenumbering="unnumbered">
+ <![CDATA[
+ #\
+ i\
+ n\
+ c\
+ l\
+ u\
+ d\
+ e\
+ &lt;\
+ s\
+ t\
+ d\
+ i\
+ o\
+ .\
+ h\
+ &gt;\
+ /*
+ */
+ i\
+ n\
+ t\
+ /*
+ */
+ m\
+ a\
+ i\
+ n(
+ ){
+ f\
+ o\
+ r(
+ i\
+ n\
+ t\
+ /*
+ */
+ i=
+ 1;
+ i&lt;
+ 1\
+ 0\
+ 0;
+ i\
+ +\
+ +)
+ if
+ (i
+ %\
+ 15
+ ==
+ 0)
+ p\
+ r\
+ i\
+ n\
+ t\
+ f(
+ "\
+ F\
+ i\
+ z\
+ z\
+ B\
+ u\
+ z\
+ z\
+ %\
+ c\
+ ",
+ 10
+ );
- /* あとは同じように普通のプログラムを変形するだけなので省略 */</programlisting>
+ /* あとは同じように普通のプログラムを変形するだけなので省略 */
+ ]]>
+ </programlisting>
<simpara>バックスラッシュを使った行継続がトークンを区切らない、というのがポイントだ。</simpara>
<simpara>さて、PHP
ではそもそもバックスラッシュを行継続に使うことができない。これにより、「3文字以上からなるトークンが一切使えない」という制約が課される。例えば、<literal>echo</literal>
@@ -180,10 +184,14 @@
<simpara>また、2文字だと文字列がまともに書けないのも辛い。<literal>''</literal> だけで
2文字使うので、「1文字の文字列リテラル」というものを書くことができない。PHP
では文字列リテラル中に生の改行が書けるので</simpara>
-<programlisting language="php" linenumbering="unnumbered">$a
-='
-a'
-;;</programlisting>
+<programlisting language="php" linenumbering="unnumbered">
+ <![CDATA[
+ $a
+ ='
+ a'
+ ;;
+ ]]>
+</programlisting>
<simpara>とすると <literal>$a</literal> は <literal>"\na"</literal> になるのだが、余計な改行が入ってしまう。</simpara>
<simpara>これらの障害をどのように乗り越えるのか、次節から見ていく。</simpara>
</section>
@@ -192,11 +200,15 @@ a'
<section xml:id="_普通の_fizzbuzz">
<title>普通の (?) fizzbuzz</title>
<simpara>まずは普通に書くとしよう。</simpara>
- <literallayout class="monospaced">&lt;?php
+ <programlisting language="php" linenumbering="unnumbered">
+ <![CDATA[
+ &lt;?php
- for ($i = 1; $i &lt; 100; $i++) {
- echo (($i % 3 ? '' : 'Fizz') . ($i % 5 ? '' : 'Buzz') ?: $i) . "\n";
- }</literallayout>
+ for ($i = 1; $i &lt; 100; $i++) {
+ echo (($i % 3 ? '' : 'Fizz') . ($i % 5 ? '' : 'Buzz') ?: $i) . "\n";
+ }
+ ]]>
+ </programlisting>
<simpara>素直に書いた fizzbuzz
とは言い難いが、このくらいは普通だということにしておかないと、この先がやっていられないので許してほしい。</simpara>
</section>
@@ -205,14 +217,18 @@ a'
<simpara><literal>for</literal>
は、3文字もある長いキーワードである。こんなものは使えない。<literal>array_</literal>
系の関数を使って、適当に置き換えるとしよう。</simpara>
- <programlisting language="php" linenumbering="unnumbered">&lt;?php
+ <programlisting language="php" linenumbering="unnumbered">
+ <![CDATA[
+ &lt;?php
- $s = range(1, 100);
- array_walk(
- $s,
- fn($i) =&gt;
- printf((($i % 3 ? '' : 'Fizz') . ($i % 5 ? '' : 'Buzz') ?: $i) . "\n"),
- );</programlisting>
+ $s = range(1, 100);
+ array_walk(
+ $s,
+ fn($i) =&gt;
+ printf((($i % 3 ? '' : 'Fizz') . ($i % 5 ? '' : 'Buzz') ?: $i) . "\n"),
+ );
+ ]]>
+ </programlisting>
<simpara><literal>array_walk</literal> や <literal>range</literal>、<literal>printf</literal> といった <literal>for</literal>
よりも長いトークンが現れてしまったが、これは次節で直すことにする。なお、<literal>echo</literal>
は文 (statement) であり式 (expression) ではないので、式である <literal>printf</literal>
@@ -223,18 +239,22 @@ a'
<simpara><literal>range</literal>、<literal>array_walk</literal>、<literal>printf</literal>
は長すぎるのでどうにかせねばならない。ここで、PHP
の可変関数を使う。可変関数とは、関数名が文字列として入った変数を経由して、関数を呼び出す機能である。</simpara>
- <programlisting language="php" linenumbering="unnumbered">&lt;?php
+ <programlisting language="php" linenumbering="unnumbered">
+ <![CDATA[
+ &lt;?php
- $r = 'range';
- $w = 'array_walk';
- $p = 'printf';
+ $r = 'range';
+ $w = 'array_walk';
+ $p = 'printf';
- $s = $r(1, 100);
- $w(
- $s,
- fn($i) =&gt;
- $p((($i % 3 ? '' : 'Fizz') . ($i % 5 ? '' : 'Buzz') ?: $i) . "\n"),
- );</programlisting>
+ $s = $r(1, 100);
+ $w(
+ $s,
+ fn($i) =&gt;
+ $p((($i % 3 ? '' : 'Fizz') . ($i % 5 ? '' : 'Buzz') ?: $i) . "\n"),
+ );
+ ]]>
+ </programlisting>
<simpara>これで関数を呼び出している所は短くなった。では、<literal>$r</literal> や <literal>$w</literal> や
<literal>$p</literal>、また <literal>'Fizz'</literal> や <literal>'Buzz'</literal> はどうやって
1行2文字に収めるのか。次のテクニックへ移ろう。</simpara>
@@ -252,23 +272,31 @@ a'
<simpara>というルールがない場合、「未定義の定数が評価された場合、その定数の名前が値になる」という
PHP 7.x までの仕様が利用できる。例えば、 <literal>Fizz</literal>
という文字列が欲しければ、次のようにする。</simpara>
-<programlisting language="php" linenumbering="unnumbered">$f
-=F
-.i
-.z
-.z
-;;</programlisting>
+<programlisting language="php" linenumbering="unnumbered">
+ <![CDATA[
+ $f
+ =F
+ .i
+ .z
+ .z
+ ;;
+ ]]>
+</programlisting>
<simpara>こうして簡単に文字列を作れる。なお、この仕様は 7.x
時点でも警告を受けるので、<literal>@</literal> 演算子を使って抑制してやるとよい。</simpara>
-<programlisting language="php" linenumbering="unnumbered">$f
-=@
-F.
-@i
-.#
-@z
-.#
-@z
-;;</programlisting>
+<programlisting language="php" linenumbering="unnumbered">
+ <![CDATA[
+ $f
+ =@
+ F.
+ @i
+ .#
+ @z
+ .#
+ @z
+ ;;
+ ]]>
+</programlisting>
<simpara>むしろ、このことがわかっていたからこそ PHP 8.x
での動作を要件に課したところがある。</simpara>
</section>
@@ -278,56 +306,72 @@ F.
<simpara>ずばり、文字列同士のビット演算を使う。PHP では、文字列同士でビット演算
(<literal>&amp;</literal>、<literal>|</literal>、<literal>^</literal>)
をした場合、文字列の各バイトごとに指定したビット演算がなされ、それを結合したものが演算結果となる。</simpara>
-<programlisting language="php" linenumbering="unnumbered">$a = "12345";
-$b = "world";
+<programlisting language="php" linenumbering="unnumbered">
+ <![CDATA[
+ $a = "12345";
+ $b = "world";
-// $a ^ $b は次のコードと同じ
-$result = '';
-for ($i = 0; $i &lt; min(strlen($a), strlen($b)); $i++) {
-$result .= $a[$i] ^ $b[$i];
-}
+ // $a ^ $b は次のコードと同じ
+ $result = '';
+ for ($i = 0; $i &lt; min(strlen($a), strlen($b)); $i++) {
+ $result .= $a[$i] ^ $b[$i];
+ }
-echo $result;
-// =&gt; F]AXQ</programlisting>
+ echo $result;
+ // =&gt; F]AXQ
+ ]]>
+</programlisting>
<simpara>これを踏まえ、次のコードを見てみよう。</simpara>
-<programlisting language="php" linenumbering="unnumbered">$x = "x\nOm\n";
-$y = "\nk!\no";
-$r = $x ^ $y;
-echo "$r\n";</programlisting>
+<programlisting language="php" linenumbering="unnumbered">
+ <![CDATA[
+ $x = "x\nOm\n";
+ $y = "\nk!\no";
+ $r = $x ^ $y;
+ echo "$r\n";
+ ]]>
+</programlisting>
<simpara>実行すると、<literal>range</literal> が表示される。さて、PHP
では文字列リテラル中に生の改行を直接書いてもよいのだった
(「主な障害」の節を参照のこと)。書きかえてみよう。</simpara>
-<programlisting language="php" linenumbering="unnumbered">$x
-='x
-Om
-';
-$y
-='
-k!
-o'
-;
+<programlisting language="php" linenumbering="unnumbered">
+ <![CDATA[
+ $x
+ ='x
+ Om
+ ';
+ $y
+ ='
+ k!
+ o'
+ ;
-$r = $x ^ $y;
-echo "$r\n";</programlisting>
+ $r = $x ^ $y;
+ echo "$r\n";
+ ]]>
+</programlisting>
<simpara>さらに <literal>#</literal> を使って適当に調整すると、次のようになる。</simpara>
-<programlisting language="php" linenumbering="unnumbered">$x
-=#
-'x
-Om
-';
-$y
-='
-k!
-o'
-;#
-$r
-=#
-$x
-^#
-$y
-;#
+<programlisting language="php" linenumbering="unnumbered">
+ <![CDATA[
+ $x
+ =#
+ 'x
+ Om
+ ';
+ $y
+ ='
+ k!
+ o'
+ ;#
+ $r
+ =#
+ $x
+ ^#
+ $y
+ ;#
-echo "$r\n";</programlisting>
+ echo "$r\n";
+ ]]>
+</programlisting>
<simpara>1行あたり2文字で、<literal>range</literal>
という文字列を生成することに成功した。他の必要な文字列にも、同様の処理をほどこす。</simpara>
<simpara>備考: <literal>Buzz</literal> 中にある小文字の <literal>u</literal> は、このロジックだと non-printable
@@ -337,155 +381,159 @@ echo "$r\n";</programlisting>
<section xml:id="_完成系">
<title>完成系</title>
<simpara>完成したものがこちら。</simpara>
- <programlisting language="php" linenumbering="unnumbered">&lt;?php
+ <programlisting language="php" linenumbering="unnumbered">
+ <![CDATA[
+ &lt;?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
- )#
- =&gt;
- $p
- ((
- (#
- $i
- %3
- ?#
- ''
- :#
- $f
- ).
- (#
- $i
- %5
- ?#
- ''
- :#
- $b
- )?
- :#
- $i
- )#
- .'
- ')
- );</programlisting>
+ $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
+ )#
+ =&gt;
+ $p
+ ((
+ (#
+ $i
+ %3
+ ?#
+ ''
+ :#
+ $f
+ ).
+ (#
+ $i
+ %5
+ ?#
+ ''
+ :#
+ $b
+ )?
+ :#
+ $i
+ )#
+ .'
+ ')
+ );
+ ]]>
+ </programlisting>
</section>
<section xml:id="_感想など">
<title>感想など</title>
@@ -501,18 +549,22 @@ echo "$r\n";</programlisting>
ではバックスラッシュによる行継続が使えないと書いたが、シェルでは使える
(当然だが、呼び出されるシェルに依存する。Bash
なら大丈夫だろう。知らんけど)。</simpara>
- <programlisting language="php" linenumbering="unnumbered">&lt;?php
+ <programlisting language="php" linenumbering="unnumbered">
+ <![CDATA[
+ &lt;?php
- printf(`
- e\
- c\
- h\
- o\
- \
- 1\
- 2\
- 3\
- `);</programlisting>
+ printf(`
+ e\
+ c\
+ h\
+ o\
+ \
+ 1\
+ 2\
+ 3\
+ `);
+ ]]>
+ </programlisting>
<simpara>なお、ここでは簡単のため出力に <literal>printf</literal> をそのまま使っているが、実際には
<literal>printf</literal> という文字列を合成して可変関数で呼び出す。</simpara>
<simpara>ただし、これでは</simpara>
@@ -525,57 +577,73 @@ echo "$r\n";</programlisting>
</blockquote>
<simpara>に違反してしまう。スペースが使えないと引数とコマンドを区切れない。これは困った。</simpara>
<simpara>もうこれ以上は不可能だと思っていたのだが、この記事の執筆中に解決する方法を思いついたので載せておく。</simpara>
-<programlisting language="php" linenumbering="unnumbered">&lt;?php
+<programlisting language="php" linenumbering="unnumbered">
+ <![CDATA[
+ &lt;?php
-$c = 'chr';
+ $c = 'chr';
-${
-'_
-'}
-=#
-$c
-(#
-32
-).
-$c
-(#
-92
-);
+ ${
+ '_
+ '}
+ =#
+ $c
+ (#
+ 32
+ ).
+ $c
+ (#
+ 92
+ );
-printf(`
-e\
-c\
-h\
-o\
-${
-'_
-'}
-1\
-2\
-3\
-`);</programlisting>
+ printf(`
+ e\
+ c\
+ h\
+ o\
+ ${
+ '_
+ '}
+ 1\
+ 2\
+ 3\
+ `);
+ ]]>
+</programlisting>
<simpara>先程と同じく、<literal>chr</literal> や <literal>printf</literal> を生成する部分は長くなるので省いた。</simpara>
-<literallayout class="monospaced">${
-'_
-'}</literallayout>
+<literallayout class="monospaced">
+ <![CDATA[
+ ${
+ '_
+ '}
+ ]]>
+</literallayout>
<simpara>は変数で、中にはスペースとエスケープが入っている
(<literal>chr(32) . chr(92)</literal>)。シェルに渡されている文字列は次のようになる。</simpara>
-<literallayout class="monospaced">e\
-c\
-h\
-o\
-\
-1\
-2\
-3\</literallayout>
+<literallayout class="monospaced">
+ <![CDATA[
+ e\
+ c\
+ h\
+ o\
+ \
+ 1\
+ 2\
+ 3\
+ ]]>
+</literallayout>
<simpara>これは、前掲したコマンドと同じだ。かくして、スペースを陽に書かずにシェルをおおよそ自由に扱えるようになった。Fizzbuzz
のワンライナーくらいすぐ書けるだろうから、あとはなんとかなるだろう
(試してないけど)。</simpara>
<simpara>ということでこれは別解ということにしておく。</simpara>
<simpara>ちなみに、PHP 8.2 からは、この記法で Warning が出るようになるようだ。</simpara>
-<literallayout class="monospaced">${
-'_
-'}</literallayout>
+<literallayout class="monospaced">
+ <![CDATA[
+ ${
+ '_
+ '}
+ ]]>
+</literallayout>
<simpara>最新版で警告が出るというのも美しくないので、私としては本編の解法を推す。</simpara>
</section>
</article>