aboutsummaryrefslogtreecommitdiffhomepage
path: root/public/posts/2022-09-29/write-fizzbuzz-in-php-2-letters-per-line/index.html
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2023-03-15 01:36:13 +0900
committernsfisis <nsfisis@gmail.com>2023-03-15 01:36:58 +0900
commit98682c7a8792e7e79e487fea5024d25cee5aa310 (patch)
treefbf975077f5c1a6ff4f9eee68e4a4908eb7f54a0 /public/posts/2022-09-29/write-fizzbuzz-in-php-2-letters-per-line/index.html
parent1fa2ed103dc521698cff261c97ecf275708be58c (diff)
downloadblog.nsfisis.dev-98682c7a8792e7e79e487fea5024d25cee5aa310.tar.gz
blog.nsfisis.dev-98682c7a8792e7e79e487fea5024d25cee5aa310.tar.zst
blog.nsfisis.dev-98682c7a8792e7e79e487fea5024d25cee5aa310.zip
fix(nuldoc): <pre> contained unnecessary whitespaces inside it
Diffstat (limited to 'public/posts/2022-09-29/write-fizzbuzz-in-php-2-letters-per-line/index.html')
-rw-r--r--public/posts/2022-09-29/write-fizzbuzz-in-php-2-letters-per-line/index.html102
1 files changed, 34 insertions, 68 deletions
diff --git a/public/posts/2022-09-29/write-fizzbuzz-in-php-2-letters-per-line/index.html b/public/posts/2022-09-29/write-fizzbuzz-in-php-2-letters-per-line/index.html
index abdb48e..f7ce047 100644
--- a/public/posts/2022-09-29/write-fizzbuzz-in-php-2-letters-per-line/index.html
+++ b/public/posts/2022-09-29/write-fizzbuzz-in-php-2-letters-per-line/index.html
@@ -125,8 +125,7 @@
特に、C言語でこのような試みをおこなったことがあるかたならそう思うだろう。事実、Cでのこの制約はほとんど無意味に等しい。
</p>
- <pre class="highlight" language="c" linenumbering="unnumbered">
- <code>#\
+ <pre class="highlight" language="c" linenumbering="unnumbered"><code>#\
i\
n\
c\
@@ -199,8 +198,7 @@
10
);
- /* あとは同じように普通のプログラムを変形するだけなので省略 */</code>
- </pre>
+ /* あとは同じように普通のプログラムを変形するだけなので省略 */</code></pre>
<p>
バックスラッシュを使った行継続がトークンを区切らない、というのがポイントだ。
@@ -260,12 +258,10 @@
また、2文字だと文字列がまともに書けないのも辛い。<code>&apos;&apos;</code>だけで 2文字使うので、「1文字の文字列リテラル」というものを書くことができない。PHP では文字列リテラル中に生の改行が書けるので
</p>
- <pre class="highlight" language="php" linenumbering="unnumbered">
- <code>$a
+ <pre class="highlight" language="php" linenumbering="unnumbered"><code>$a
=&apos;
a&apos;
-;;</code>
- </pre>
+;;</code></pre>
<p>
とすると<code>$a</code>は<code>&quot;\na&quot;</code>になるのだが、余計な改行が入ってしまう。
@@ -284,13 +280,11 @@ a&apos;
まずは普通に書くとしよう。
</p>
- <pre class="monospaced highlight">
- <code>&lt;?php
+ <pre class="monospaced highlight"><code>&lt;?php
for ($i = 1; $i &lt; 100; $i++) {
echo (($i % 3 ? &apos;&apos; : &apos;Fizz&apos;) . ($i % 5 ? &apos;&apos; : &apos;Buzz&apos;) ?: $i) . &quot;\n&quot;;
- }</code>
- </pre>
+ }</code></pre>
<p>
素直に書いた fizzbuzz とは言い難いが、このくらいは普通だということにしておかないと、この先がやっていられないので許してほしい。
@@ -303,16 +297,14 @@ a&apos;
<code>for</code>は、3文字もある長いキーワードである。こんなものは使えない。<code>array_</code>系の関数を使って、適当に置き換えるとしよう。
</p>
- <pre class="highlight" language="php" linenumbering="unnumbered">
- <code>&lt;?php
+ <pre class="highlight" language="php" linenumbering="unnumbered"><code>&lt;?php
$s = range(1, 100);
array_walk(
$s,
fn($i) =&gt;
printf((($i % 3 ? &apos;&apos; : &apos;Fizz&apos;) . ($i % 5 ? &apos;&apos; : &apos;Buzz&apos;) ?: $i) . &quot;\n&quot;),
- );</code>
- </pre>
+ );</code></pre>
<p>
<code>array_walk</code>や<code>range</code>、<code>printf</code>といった<code>for</code>よりも長いトークンが現れてしまったが、これは次節で直すことにする。なお、<code>echo</code>は文 (statement) であり式 (expression) ではないので、式である<code>printf</code>に置き換えた。
@@ -325,8 +317,7 @@ a&apos;
<code>range</code>、<code>array_walk</code>、<code>printf</code>は長すぎるのでどうにかせねばならない。ここで、PHP の可変関数を使う。可変関数とは、関数名が文字列として入った変数を経由して、関数を呼び出す機能である。
</p>
- <pre class="highlight" language="php" linenumbering="unnumbered">
- <code>&lt;?php
+ <pre class="highlight" language="php" linenumbering="unnumbered"><code>&lt;?php
$r = &apos;range&apos;;
$w = &apos;array_walk&apos;;
@@ -337,8 +328,7 @@ a&apos;
$s,
fn($i) =&gt;
$p((($i % 3 ? &apos;&apos; : &apos;Fizz&apos;) . ($i % 5 ? &apos;&apos; : &apos;Buzz&apos;) ?: $i) . &quot;\n&quot;),
- );</code>
- </pre>
+ );</code></pre>
<p>
これで関数を呼び出している所は短くなった。では、<code>$r</code>や<code>$w</code>や<code>$p</code>、また<code>&apos;Fizz&apos;</code>や<code>&apos;Buzz&apos;</code>はどうやって 1行2文字に収めるのか。次のテクニックへ移ろう。
@@ -365,21 +355,18 @@ a&apos;
というルールがない場合、「未定義の定数が評価された場合、その定数の名前が値になる」という PHP 7.x までの仕様が利用できる。例えば、<code>Fizz</code>という文字列が欲しければ、次のようにする。
</p>
- <pre class="highlight" language="php" linenumbering="unnumbered">
- <code>$f
+ <pre class="highlight" language="php" linenumbering="unnumbered"><code>$f
=F
.i
.z
.z
-;;</code>
- </pre>
+;;</code></pre>
<p>
こうして簡単に文字列を作れる。なお、この仕様は 7.x 時点でも警告を受けるので、<code>@</code>演算子を使って抑制してやるとよい。
</p>
- <pre class="highlight" language="php" linenumbering="unnumbered">
- <code>$f
+ <pre class="highlight" language="php" linenumbering="unnumbered"><code>$f
=@
F.
@i
@@ -387,8 +374,7 @@ F.
@z
.#
@z
-;;</code>
- </pre>
+;;</code></pre>
<p>
むしろ、このことがわかっていたからこそ PHP 8.x での動作を要件に課したところがある。
@@ -405,8 +391,7 @@ F.
ずばり、文字列同士のビット演算を使う。PHP では、文字列同士でビット演算 (<code>&amp;</code>、<code>|</code>、<code>^</code>) をした場合、文字列の各バイトごとに指定したビット演算がなされ、それを結合したものが演算結果となる。
</p>
- <pre class="highlight" language="php" linenumbering="unnumbered">
- <code>$a = &quot;12345&quot;;
+ <pre class="highlight" language="php" linenumbering="unnumbered"><code>$a = &quot;12345&quot;;
$b = &quot;world&quot;;
// $a ^ $b は次のコードと同じ
@@ -416,26 +401,22 @@ $result .= $a[$i] ^ $b[$i];
}
echo $result;
-// =&gt; F]AXQ</code>
- </pre>
+// =&gt; F]AXQ</code></pre>
<p>
これを踏まえ、次のコードを見てみよう。
</p>
- <pre class="highlight" language="php" linenumbering="unnumbered">
- <code>$x = &quot;x\nOm\n&quot;;
+ <pre class="highlight" language="php" linenumbering="unnumbered"><code>$x = &quot;x\nOm\n&quot;;
$y = &quot;\nk!\no&quot;;
$r = $x ^ $y;
-echo &quot;$r\n&quot;;</code>
- </pre>
+echo &quot;$r\n&quot;;</code></pre>
<p>
実行すると、<code>range</code>が表示される。さて、PHP では文字列リテラル中に生の改行を直接書いてもよいのだった (「主な障害」の節を参照のこと)。書きかえてみよう。
</p>
- <pre class="highlight" language="php" linenumbering="unnumbered">
- <code>$x
+ <pre class="highlight" language="php" linenumbering="unnumbered"><code>$x
=&apos;x
Om
&apos;;
@@ -446,15 +427,13 @@ o&apos;
;
$r = $x ^ $y;
-echo &quot;$r\n&quot;;</code>
- </pre>
+echo &quot;$r\n&quot;;</code></pre>
<p>
さらに<code>#</code>を使って適当に調整すると、次のようになる。
</p>
- <pre class="highlight" language="php" linenumbering="unnumbered">
- <code>$x
+ <pre class="highlight" language="php" linenumbering="unnumbered"><code>$x
=#
&apos;x
Om
@@ -471,8 +450,7 @@ $x
$y
;#
-echo &quot;$r\n&quot;;</code>
- </pre>
+echo &quot;$r\n&quot;;</code></pre>
<p>
1行あたり2文字で、<code>range</code>という文字列を生成することに成功した。他の必要な文字列にも、同様の処理をほどこす。
@@ -490,8 +468,7 @@ echo &quot;$r\n&quot;;</code>
完成したものがこちら。
</p>
- <pre class="highlight" language="php" linenumbering="unnumbered">
- <code>&lt;?php
+ <pre class="highlight" language="php" linenumbering="unnumbered"><code>&lt;?php
$x
=#
@@ -639,8 +616,7 @@ echo &quot;$r\n&quot;;</code>
)#
.&apos;
&apos;)
- );</code>
- </pre>
+ );</code></pre>
</section>
<section id="section--_感想など">
@@ -660,8 +636,7 @@ echo &quot;$r\n&quot;;</code>
PHP では、バッククォートを使ってシェルを呼び出せる。これは<code>shell_exec</code>関数と等価である。さて、PHP ではバックスラッシュによる行継続が使えないと書いたが、シェルでは使える (当然だが、呼び出されるシェルに依存する。Bash なら大丈夫だろう。知らんけど)。
</p>
- <pre class="highlight" language="php" linenumbering="unnumbered">
- <code>&lt;?php
+ <pre class="highlight" language="php" linenumbering="unnumbered"><code>&lt;?php
printf(`
e\
@@ -672,8 +647,7 @@ echo &quot;$r\n&quot;;</code>
1\
2\
3\
- `);</code>
- </pre>
+ `);</code></pre>
<p>
なお、ここでは簡単のため出力に<code>printf</code>をそのまま使っているが、実際には<code>printf</code>という文字列を合成して可変関数で呼び出す。
@@ -701,8 +675,7 @@ echo &quot;$r\n&quot;;</code>
もうこれ以上は不可能だと思っていたのだが、この記事の執筆中に解決する方法を思いついたので載せておく。
</p>
- <pre class="highlight" language="php" linenumbering="unnumbered">
- <code>&lt;?php
+ <pre class="highlight" language="php" linenumbering="unnumbered"><code>&lt;?php
$c = &apos;chr&apos;;
@@ -730,33 +703,28 @@ ${
1\
2\
3\
-`);</code>
- </pre>
+`);</code></pre>
<p>
先程と同じく、<code>chr</code>や<code>printf</code>を生成する部分は長くなるので省いた。
</p>
- <pre class="monospaced highlight">
- <code>${
+ <pre class="monospaced highlight"><code>${
&apos;_
-&apos;}</code>
- </pre>
+&apos;}</code></pre>
<p>
は変数で、中にはスペースとエスケープが入っている (<code>chr(32) . chr(92)</code>)。シェルに渡されている文字列は次のようになる。
</p>
- <pre class="monospaced highlight">
- <code>e\
+ <pre class="monospaced highlight"><code>e\
c\
h\
o\
\
1\
2\
-3\</code>
- </pre>
+3\</code></pre>
<p>
これは、前掲したコマンドと同じだ。かくして、スペースを陽に書かずにシェルをおおよそ自由に扱えるようになった。Fizzbuzz のワンライナーくらいすぐ書けるだろうから、あとはなんとかなるだろう (試してないけど)。
@@ -770,11 +738,9 @@ o\
ちなみに、PHP 8.2 からは、この記法で Warning が出るようになるようだ。
</p>
- <pre class="monospaced highlight">
- <code>${
+ <pre class="monospaced highlight"><code>${
&apos;_
-&apos;}</code>
- </pre>
+&apos;}</code></pre>
<p>
最新版で警告が出るというのも美しくないので、私としては本編の解法を推す。