From a65bb9609284d273f0aa232dbaf69597c87f5a12 Mon Sep 17 00:00:00 2001 From: nsfisis Date: Sun, 15 Jun 2025 13:12:46 +0900 Subject: feat(blog/nuldoc): merge consecutive text nodes --- vhosts/blog/nuldoc-src/djot/to_html.ts | 39 ++++++++++ .../posts/2021-03-30/phperkaigi-2021/index.html | 10 +-- .../index.html | 4 +- .../python-unbound-local-error/index.html | 4 +- .../ruby-detect-running-implementation/index.html | 2 +- .../ruby-then-keyword-and-case-in/index.html | 4 +- .../rust-where-are-primitive-types-from/index.html | 6 +- .../index.html | 2 +- .../vim-swap-order-of-selected-lines/index.html | 2 +- .../2022-04-09/phperkaigi-2022-tokens/index.html | 18 ++--- .../index.html | 16 ++--- .../posts/2022-05-01/phperkaigi-2022/index.html | 14 ++-- .../php-conference-okinawa-code-golf/index.html | 10 +-- .../index.html | 6 +- .../index.html | 38 +++++----- .../phperkaigi-2023-unused-token-quiz-1/index.html | 16 ++--- .../setup-server-for-this-site/index.html | 14 ++-- .../phperkaigi-2023-unused-token-quiz-2/index.html | 2 +- .../phperkaigi-2023-unused-token-quiz-3/index.html | 4 +- .../rewrite-this-blog-generator/index.html | 12 ++-- .../index.html | 12 ++-- .../2023-04-04/phperkaigi-2023-report/index.html | 22 +++--- .../2023-06-25/phpconfuk-2023-report/index.html | 4 +- .../compile-php-runtime-to-wasm/index.html | 40 +++++------ .../index.html | 2 +- .../public/posts/2023-12-03/isucon-13/index.html | 8 +-- .../posts/2023-12-31/2023-reflections/index.html | 4 +- .../index.html | 10 +-- .../2024-02-22/phpkansai-2024-report/index.html | 2 +- .../2024-03-17/phperkaigi-2024-report/index.html | 10 +-- .../phpcon-odawara-2024-report/index.html | 2 +- .../pipefail-option-in-gitlab-ci-cd/index.html | 8 +-- .../index.html | 16 ++--- .../2024-06-19/scalamatsuri-2024-report/index.html | 2 +- .../reparojson-fix-only-json-formatter/index.html | 8 +-- .../index.html | 8 +-- .../posts/2024-12-04/cohackpp-report/index.html | 4 +- .../posts/2024-12-33/2024-reflections/index.html | 8 +-- .../phperkaigi-2023-tokens-q1/index.html | 32 ++++----- .../index.html | 8 +-- .../phpcon-nagoya-2025-report/index.html | 2 +- .../index.html | 4 +- .../http-1-1-send-multiple-same-headers/index.html | 6 +- .../trick-2025-most-ruby-on-ruby-award/index.html | 8 +-- .../index.html | 10 +-- .../make-tiny-self-hosted-c-compiler/index.html | 24 +++---- .../public/posts/2025-06-14/baba-is-you/index.html | 82 +++++++++++----------- 47 files changed, 304 insertions(+), 265 deletions(-) diff --git a/vhosts/blog/nuldoc-src/djot/to_html.ts b/vhosts/blog/nuldoc-src/djot/to_html.ts index 3a6c1e9f..5ea9b57d 100644 --- a/vhosts/blog/nuldoc-src/djot/to_html.ts +++ b/vhosts/blog/nuldoc-src/djot/to_html.ts @@ -13,6 +13,7 @@ import { } from "../dom.ts"; export default async function toHtml(doc: Document): Promise { + mergeConsecutiveTextNodes(doc); removeUnnecessaryTextNode(doc); transformLinkLikeToAnchorElement(doc); transformSectionIdAttribute(doc); @@ -23,9 +24,47 @@ export default async function toHtml(doc: Document): Promise { traverseFootnotes(doc); removeUnnecessaryParagraphNode(doc); await transformAndHighlightCodeBlockElement(doc); + mergeConsecutiveTextNodes(doc); return doc; } +function mergeConsecutiveTextNodes(doc: Document) { + forEachChildRecursively(doc.root, (n) => { + if (n.kind !== "element") { + return; + } + + const newChildren: Node[] = []; + let currentTextContent = ""; + + for (const child of n.children) { + if (child.kind === "text" && !child.raw) { + currentTextContent += child.content; + } else { + if (currentTextContent !== "") { + newChildren.push({ + kind: "text", + content: currentTextContent, + raw: false, + }); + currentTextContent = ""; + } + newChildren.push(child); + } + } + + if (currentTextContent !== "") { + newChildren.push({ + kind: "text", + content: currentTextContent, + raw: false, + }); + } + + n.children = newChildren; + }); +} + function removeUnnecessaryTextNode(doc: Document) { forEachChildRecursively(doc.root, (n) => { if (n.kind !== "element") { diff --git a/vhosts/blog/public/posts/2021-03-30/phperkaigi-2021/index.html b/vhosts/blog/public/posts/2021-03-30/phperkaigi-2021/index.html index 14117f8c..8459a549 100644 --- a/vhosts/blog/public/posts/2021-03-30/phperkaigi-2021/index.html +++ b/vhosts/blog/public/posts/2021-03-30/phperkaigi-2021/index.html @@ -69,7 +69,7 @@

PHPerKaigi 2021 参加レポ

- 2021-03-26 から 2021-03-28 にかけて開催された、PHPerKaigi 2021 に一般参加者として参加した。 弊社デジタルサーカス株式会社 (今年1月から勤務) はダイヤモンドスポンサーとなっており、スポンサー枠のチケットを使わせていただいた。 + 2021-03-26 から 2021-03-28 にかけて開催された、PHPerKaigi 2021 に一般参加者として参加した。弊社デジタルサーカス株式会社 (今年1月から勤務) はダイヤモンドスポンサーとなっており、スポンサー枠のチケットを使わせていただいた。

このようなカンファレンスには初めて参加するのでかねてより心待ちにしていたのだが、生憎2日目から体調を崩してしまい、この記事も途中までとなっている。まだ見ていないセッションも多いが、ひとまず現時点での参加レポを書いておく。 @@ -112,7 +112,7 @@ 個人的に楽しみだった発表。

- 期待通りの興味深い発表だった。FUSE 自体も今回の発表で知ったのだが、これ本体の実装を見るのも面白そうだ。 この発表を聞きながらファイルシステムにマウントできそうなものを考えていたのだが、およそ木構造をしているものすべてと言えそうだ (ハンマーしか持っていないと云々)。何かできそうだがなかなか思いつかない。 + 期待通りの興味深い発表だった。FUSE 自体も今回の発表で知ったのだが、これ本体の実装を見るのも面白そうだ。この発表を聞きながらファイルシステムにマウントできそうなものを考えていたのだが、およそ木構造をしているものすべてと言えそうだ (ハンマーしか持っていないと云々)。何かできそうだがなかなか思いつかない。

@@ -121,7 +121,7 @@

10:50 [A] 実践ATDD 〜TDDから更に歩みを進めたソフトウェア開発へ〜

- User Acceptance Test (UAT) くらいの規模になると個人開発・趣味開発では触れない領域なので、大いに勉強になった。スライドに添付されている資料が相当に充実していたので、これを読むのが本番といった様相すら感じる。 高レベルテストの自動化は現在のプロジェクトでも感じており、自動化のチャンスは伺っている。とはいえセッションでも指摘されているように自動化することにコストがかかりすぎる領域があるのも事実で、そのバランスが難しい。 + User Acceptance Test (UAT) くらいの規模になると個人開発・趣味開発では触れない領域なので、大いに勉強になった。スライドに添付されている資料が相当に充実していたので、これを読むのが本番といった様相すら感じる。高レベルテストの自動化は現在のプロジェクトでも感じており、自動化のチャンスは伺っている。とはいえセッションでも指摘されているように自動化することにコストがかかりすぎる領域があるのも事実で、そのバランスが難しい。

@@ -130,7 +130,7 @@ 型のある世界で生きてきた身として大いに楽しみにしていた発表。

- 昨今、動的型付き言語での型宣言・型アノテーション・型ヒントの導入が相次いでいる。長らく静的型付き言語を書いてきた私からすると、ようやく気づいたかといったところだが、ともかく型を導入する言語が増えてきた。 今のプロジェクトでも新しく追加するコードには型をつけるよう努めているが、どうしても古いコードには型がついていない。個人的には型のないコードに対してどう型を自動的に付けるかという点に興味があり、その点で Ruby の typeprof には注目している。 + 昨今、動的型付き言語での型宣言・型アノテーション・型ヒントの導入が相次いでいる。長らく静的型付き言語を書いてきた私からすると、ようやく気づいたかといったところだが、ともかく型を導入する言語が増えてきた。今のプロジェクトでも新しく追加するコードには型をつけるよう努めているが、どうしても古いコードには型がついていない。個人的には型のないコードに対してどう型を自動的に付けるかという点に興味があり、その点で Ruby の typeprof には注目している。

@@ -191,7 +191,7 @@ 今回、雑談/登壇者への質問等向けに Discord サーバもあったのだが、こちらは参加こそしたものの ROM のままになってしまった。発表に1ウィンドウ、メモを書くのに1ウィンドウ、Discord 表示に 1ウィンドウで私にはもう脳のリソースとディスプレイのスペースが追いつかなかった (さらにいうと Zoom でアンカンファレンスもやっていたようだ。こちらはまったく参加していない)。

- 1つ個人的な反省点としては、一つ一つのセッションを真剣に聞き過ぎたというものがある。もっと適当に聞いておけばよかった。これだけだと大変語弊があるのだが、言い方を変えると、Discord しかりアンカンファレンスしかり「このイベントのこの瞬間にしかないコンテンツ」に触れずに、後から見返せる発表やスライドに注力してしまった、ということだ。発表の詳細な見直しはあとからできるのだから、今しかできないことを考えるべきだった。 まあ初カンファレンスだし、とお茶を濁しておこう。 + 1つ個人的な反省点としては、一つ一つのセッションを真剣に聞き過ぎたというものがある。もっと適当に聞いておけばよかった。これだけだと大変語弊があるのだが、言い方を変えると、Discord しかりアンカンファレンスしかり「このイベントのこの瞬間にしかないコンテンツ」に触れずに、後から見返せる発表やスライドに注力してしまった、ということだ。発表の詳細な見直しはあとからできるのだから、今しかできないことを考えるべきだった。まあ初カンファレンスだし、とお茶を濁しておこう。

さて、カンファレンスで一つ気になったことがある。それは、Discord という書き込み場所が増えたことでニコ生のコメントの流量が吸い取られてしまったのではないか、という点だ。ニコニコだけ見ていると過疎っているかのように見えた発表も、Discord の方では盛り上がっている、というのを何度か見かけた。ニコニコのコメント方式は盛り上がりを如実に反映するが、逆もまたしかり。Discord があったこと自体はプラスだったと思うが、この点はマイナスだったのではないかと感じる。 diff --git a/vhosts/blog/public/posts/2021-10-02/cpp-you-can-use-keywords-in-attributes/index.html b/vhosts/blog/public/posts/2021-10-02/cpp-you-can-use-keywords-in-attributes/index.html index 93f9d779..ff62c2e9 100644 --- a/vhosts/blog/public/posts/2021-10-02/cpp-you-can-use-keywords-in-attributes/index.html +++ b/vhosts/blog/public/posts/2021-10-02/cpp-you-can-use-keywords-in-attributes/index.html @@ -66,7 +66,7 @@

- この記事は Qiita から移植してきたものです。 元 URL: https://qiita.com/nsfisis/items/94090937bcf860cfa93b + この記事は Qiita から移植してきたものです。元 URL: https://qiita.com/nsfisis/items/94090937bcf860cfa93b

@@ -128,7 +128,7 @@

- キーワードでも属性として指定する場合は非キーワードとして使えるらしい。 実際にやってみる。 + キーワードでも属性として指定する場合は非キーワードとして使えるらしい。実際にやってみる。

同サイトの keywords のページ から一覧を拝借し、上のコードが出来上がった (C++17 においてキーワードでないものなど、一部省いている)。 大量の警告 (unknown attribute `〇〇’ ignored) がコンパイラから出力されるが、コンパイルできる。 diff --git a/vhosts/blog/public/posts/2021-10-02/python-unbound-local-error/index.html b/vhosts/blog/public/posts/2021-10-02/python-unbound-local-error/index.html index 7cc28941..0e8c595b 100644 --- a/vhosts/blog/public/posts/2021-10-02/python-unbound-local-error/index.html +++ b/vhosts/blog/public/posts/2021-10-02/python-unbound-local-error/index.html @@ -66,7 +66,7 @@

- この記事は Qiita から移植してきたものです。 元 URL: https://qiita.com/nsfisis/items/5d733703afcb35bbf399 + この記事は Qiita から移植してきたものです。元 URL: https://qiita.com/nsfisis/items/5d733703afcb35bbf399

@@ -94,7 +94,7 @@

- local変数 x が代入前に参照された、とある。これは、fx を参照するのではなく、新しく別の変数を g 内に作ってしまっているため。 前述のコードを宣言と代入を便宜上分けて書き直すと次のようになる。var を変数宣言のための構文として擬似的に利用している。 + local変数 x が代入前に参照された、とある。これは、fx を参照するのではなく、新しく別の変数を g 内に作ってしまっているため。前述のコードを宣言と代入を便宜上分けて書き直すと次のようになる。var を変数宣言のための構文として擬似的に利用している。

# 注: var は正しい Python の文法ではない。上記参照のこと
diff --git a/vhosts/blog/public/posts/2021-10-02/ruby-detect-running-implementation/index.html b/vhosts/blog/public/posts/2021-10-02/ruby-detect-running-implementation/index.html
index 32d472dc..d8effaf6 100644
--- a/vhosts/blog/public/posts/2021-10-02/ruby-detect-running-implementation/index.html
+++ b/vhosts/blog/public/posts/2021-10-02/ruby-detect-running-implementation/index.html
@@ -63,7 +63,7 @@
             

- この記事は Qiita から移植してきたものです。 元 URL: https://qiita.com/nsfisis/items/74d7ffeeebc51b20d791 + この記事は Qiita から移植してきたものです。元 URL: https://qiita.com/nsfisis/items/74d7ffeeebc51b20d791

diff --git a/vhosts/blog/public/posts/2021-10-02/ruby-then-keyword-and-case-in/index.html b/vhosts/blog/public/posts/2021-10-02/ruby-then-keyword-and-case-in/index.html index f266c59b..e8da5364 100644 --- a/vhosts/blog/public/posts/2021-10-02/ruby-then-keyword-and-case-in/index.html +++ b/vhosts/blog/public/posts/2021-10-02/ruby-then-keyword-and-case-in/index.html @@ -66,7 +66,7 @@

- この記事は Qiita から移植してきたものです。 元 URL: https://qiita.com/nsfisis/items/787a8cf888a304497223 + この記事は Qiita から移植してきたものです。元 URL: https://qiita.com/nsfisis/items/787a8cf888a304497223

@@ -155,7 +155,7 @@
if a b end

- then; も改行もないのでエラーになるが、これは条件式がどこまで続いているのかわからないためだ。 この例は二通りに解釈できる。 + then; も改行もないのでエラーになるが、これは条件式がどこまで続いているのかわからないためだ。この例は二通りに解釈できる。

# a という変数かメソッドの評価結果が truthy なら b という変数かメソッドを評価
diff --git a/vhosts/blog/public/posts/2021-10-02/rust-where-are-primitive-types-from/index.html b/vhosts/blog/public/posts/2021-10-02/rust-where-are-primitive-types-from/index.html
index 26ab8bda..a480b6a4 100644
--- a/vhosts/blog/public/posts/2021-10-02/rust-where-are-primitive-types-from/index.html
+++ b/vhosts/blog/public/posts/2021-10-02/rust-where-are-primitive-types-from/index.html
@@ -63,7 +63,7 @@
             

- この記事は Qiita から移植してきたものです。 元 URL: https://qiita.com/nsfisis/items/9a429432258bbcd6c565 + この記事は Qiita から移植してきたものです。元 URL: https://qiita.com/nsfisis/items/9a429432258bbcd6c565

@@ -116,7 +116,7 @@ 大雑把な構造としては、compiler フォルダ以下に rustc_* という名前のクレートが数十個入っている。これがどうやら rustc コマンドの実装部のようだ。

- rustc はセルフホストされている (= rustc 自身が Rust で書かれている) ので、boolchar などで適当に検索をかけてもノイズが多すぎて話にならない。 しかし、お誂え向きなことに i128/u128 というコンパイラ自身が使うことがなさそうな型が存在するのでこれを使って git grep してみる。 + rustc はセルフホストされている (= rustc 自身が Rust で書かれている) ので、boolchar などで適当に検索をかけてもノイズが多すぎて話にならない。しかし、お誂え向きなことに i128/u128 というコンパイラ自身が使うことがなさそうな型が存在するのでこれを使って git grep してみる。

$ git grep "\bi128\b" | wc      # i128
@@ -212,7 +212,7 @@
 }

- 関数名や doc comment が示している通り、この関数は識別子 (identifier, ident) を現在のレキシカルスコープ内で解決 (resolve) する。 if ns == TypeNS のブロック内では、primitive_type_table (上記の PrimitiveTypeTable::new() で作られた変数) に含まれている識別子 (booli32 など) かどうか判定し、そうであればそれに紐づけられたプリミティブ型を返している。 + 関数名や doc comment が示している通り、この関数は識別子 (identifier, ident) を現在のレキシカルスコープ内で解決 (resolve) する。if ns == TypeNS のブロック内では、primitive_type_table (上記の PrimitiveTypeTable::new() で作られた変数) に含まれている識別子 (booli32 など) かどうか判定し、そうであればそれに紐づけられたプリミティブ型を返している。

なお、ns は「名前空間」を示す変数である。Rust における名前空間はC言語におけるそれとほとんど同じで、今探している名前が関数名/変数名なのか型なのかマクロなのかを区別している。この if は、プリミティブ型に解決されるのは型を探しているときだけだ、と言っている。 diff --git a/vhosts/blog/public/posts/2021-10-02/vim-difference-between-autocmd-bufwrite-and-bufwritepre/index.html b/vhosts/blog/public/posts/2021-10-02/vim-difference-between-autocmd-bufwrite-and-bufwritepre/index.html index c3d61b1b..b35db7fb 100644 --- a/vhosts/blog/public/posts/2021-10-02/vim-difference-between-autocmd-bufwrite-and-bufwritepre/index.html +++ b/vhosts/blog/public/posts/2021-10-02/vim-difference-between-autocmd-bufwrite-and-bufwritepre/index.html @@ -63,7 +63,7 @@

- この記事は Qiita から移植してきたものです。 元 URL: https://qiita.com/nsfisis/items/79ab4db8564032de0b25 + この記事は Qiita から移植してきたものです。元 URL: https://qiita.com/nsfisis/items/79ab4db8564032de0b25

diff --git a/vhosts/blog/public/posts/2021-10-02/vim-swap-order-of-selected-lines/index.html b/vhosts/blog/public/posts/2021-10-02/vim-swap-order-of-selected-lines/index.html index 5f9a5784..ffbc2abc 100644 --- a/vhosts/blog/public/posts/2021-10-02/vim-swap-order-of-selected-lines/index.html +++ b/vhosts/blog/public/posts/2021-10-02/vim-swap-order-of-selected-lines/index.html @@ -63,7 +63,7 @@

- この記事は Qiita から移植してきたものです。 元 URL: https://qiita.com/nsfisis/items/4fefb361d9a693803520 + この記事は Qiita から移植してきたものです。元 URL: https://qiita.com/nsfisis/items/4fefb361d9a693803520

diff --git a/vhosts/blog/public/posts/2022-04-09/phperkaigi-2022-tokens/index.html b/vhosts/blog/public/posts/2022-04-09/phperkaigi-2022-tokens/index.html index 581e229f..46ccdb89 100644 --- a/vhosts/blog/public/posts/2022-04-09/phperkaigi-2022-tokens/index.html +++ b/vhosts/blog/public/posts/2022-04-09/phperkaigi-2022-tokens/index.html @@ -263,7 +263,7 @@

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

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

assert(0 === +!![]);
@@ -282,7 +282,7 @@
               

if 文なしで条件分岐

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

@@ -344,7 +344,7 @@ }

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

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

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

@@ -521,10 +521,10 @@

プログラム全体

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

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

@@ -536,13 +536,13 @@

状態保持

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

ROT 13

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

それにしてもなぜこんなものが標準ライブラリに……。 @@ -556,7 +556,7 @@ 解いていただいたみなさん、また、難易度調整につきあっていただいた社内のみなさん、ありがとうございました。

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

実はもう作りはじめているので、どうか来年もありますように……。 diff --git a/vhosts/blog/public/posts/2022-04-24/term-banner-write-tool-showing-banner-in-terminal/index.html b/vhosts/blog/public/posts/2022-04-24/term-banner-write-tool-showing-banner-in-terminal/index.html index 5f0cee0a..87bf72ba 100644 --- a/vhosts/blog/public/posts/2022-04-24/term-banner-write-tool-showing-banner-in-terminal/index.html +++ b/vhosts/blog/public/posts/2022-04-24/term-banner-write-tool-showing-banner-in-terminal/index.html @@ -79,16 +79,16 @@ 以前、big-clock-mode という似たようなプログラムを書いた。 これは tmux の :clock-mode コマンドに着想を得たもので、:clock-mode よりも大きく現在時刻を表示する。

- big-clock-mode を開発したのは、次のようなシチュエーションで使うためである。 弊社では現在リモートワークが基本だが、web 会議などで画面共有しているときに、休憩を挟んで特定の時刻から再開する、ということがある。 こういったケースで、画面上に現在の時刻を大きめに表示しておくと、モニタから離れても遠くから時刻がわかるので便利である。 + big-clock-mode を開発したのは、次のようなシチュエーションで使うためである。弊社では現在リモートワークが基本だが、web 会議などで画面共有しているときに、休憩を挟んで特定の時刻から再開する、ということがある。こういったケースで、画面上に現在の時刻を大きめに表示しておくと、モニタから離れても遠くから時刻がわかるので便利である。

それこそタイマアプリか何かを使えばいいのだが、ターミナルに棲むいきものとしては、住処から離れたくないわけだ。

- しばらく便利に使っていたのだが、ひとつ不満点が出てきた。それは、再開する時刻がいつだったかを覚えておかなければならないということだ。 どこかにメモしておいてもいいが、せっかくなら現在時刻とともに表示させておきたい。 + しばらく便利に使っていたのだが、ひとつ不満点が出てきた。それは、再開する時刻がいつだったかを覚えておかなければならないということだ。どこかにメモしておいてもいいが、せっかくなら現在時刻とともに表示させておきたい。

- そんなわけで、「任意の文字列をターミナルに表示する」プログラムを書く運びとなった。 まあ、作らなくても探せばあると思うが、作りたいものは作りたいので知ったことではない。 + そんなわけで、「任意の文字列をターミナルに表示する」プログラムを書く運びとなった。まあ、作らなくても探せばあると思うが、作りたいものは作りたいので知ったことではない。

@@ -111,7 +111,7 @@ big-clock-mode が Go 製なので、今回も Go で書いた。 PNG が標準ライブラリにあったり、Shift-JIS のエンコーディングが準標準ライブラリにあったりしたのは助かった。

- フォントファイルは go:embed で実行ファイルに埋め込んでいるので、ビルド後はワンバイナリで動く。 仕事ではスクリプト言語ばかり書いているが、やはりコンパイル言語はいい。 + フォントファイルは go:embed で実行ファイルに埋め込んでいるので、ビルド後はワンバイナリで動く。仕事ではスクリプト言語ばかり書いているが、やはりコンパイル言語はいい。

@@ -120,16 +120,16 @@ フリーの 8x8 ビットマップフォントである、 美咲フォント 2021-05-05a 版 を使わせていただいた。

- はじめは自分でポチポチ打っていたのだが、「き」くらいまでやって挫折した。 同じく 8x8 で作っていたのだが、平仮名でさえも、この小さなキャンバスにはとても収められない。 + はじめは自分でポチポチ打っていたのだが、「き」くらいまでやって挫折した。同じく 8x8 で作っていたのだが、平仮名でさえも、この小さなキャンバスにはとても収められない。

- 美咲フォントは、平仮名・片仮名に留まらず、JIS 第一・第二水準の漢字までサポートしている。 第二水準ともなると一生お目にかかることのない字の方が多いくらいだが、これをこの大きさで書くというのは、もはや芸術の域である。 + 美咲フォントは、平仮名・片仮名に留まらず、JIS 第一・第二水準の漢字までサポートしている。第二水準ともなると一生お目にかかることのない字の方が多いくらいだが、これをこの大きさで書くというのは、もはや芸術の域である。

- さらに言うと、実のところ美咲フォントは実サイズ 7x7 で作られており、余白が設けられている。 これは、単純にそのまま並べても字間・行間を確保できるようにという配慮である。 おかげでコーディングまで楽になった。 + さらに言うと、実のところ美咲フォントは実サイズ 7x7 で作られており、余白が設けられている。これは、単純にそのまま並べても字間・行間を確保できるようにという配慮である。おかげでコーディングまで楽になった。

- ゴシック体と明朝体があったが、私の好みで明朝体の方にした。 ただ、ゴシック体の方が見やすい気がするので、フォントを選べるように後ほど拡張するかもしれない。 + ゴシック体と明朝体があったが、私の好みで明朝体の方にした。ただ、ゴシック体の方が見やすい気がするので、フォントを選べるように後ほど拡張するかもしれない。

diff --git a/vhosts/blog/public/posts/2022-05-01/phperkaigi-2022/index.html b/vhosts/blog/public/posts/2022-05-01/phperkaigi-2022/index.html index b593e374..c87a9287 100644 --- a/vhosts/blog/public/posts/2022-05-01/phperkaigi-2022/index.html +++ b/vhosts/blog/public/posts/2022-05-01/phperkaigi-2022/index.html @@ -66,7 +66,7 @@

はじめに

- 2022-04-09 から 2022-04-11 にかけて開催された、 PHPerKaigi 2022 に、 一般参加者として参加した。 弊社 デジタルサーカス株式会社 はダイヤモンドスポンサーとなっており、 スポンサー枠のチケットを使わせていただいた。 + 2022-04-09 から 2022-04-11 にかけて開催された、 PHPerKaigi 2022 に、一般参加者として参加した。弊社 デジタルサーカス株式会社 はダイヤモンドスポンサーとなっており、スポンサー枠のチケットを使わせていただいた。

昨年のレポートは こちら 。 @@ -77,7 +77,7 @@

厳選おすすめトーク

- 多くの素晴らしいトークの中から、特におすすめのものを 5つ選んだ。 是非聞いてほしい。引用部分は、リンク先プロポーザルから引用している。 + 多くの素晴らしいトークの中から、特におすすめのものを 5つ選んだ。是非聞いてほしい。引用部分は、リンク先プロポーザルから引用している。

予防に勝る防御なし - 堅牢なコードを導く様々な設計のヒント @@ -181,13 +181,13 @@

トークン問題の作成

- 今回は、PHPer チャレンジ用に弊社のトークン問題を 3題作成した。 こちらについては 別途記事にしている ので、そちらを参照されたい。 + 今回は、PHPer チャレンジ用に弊社のトークン問題を 3題作成した。こちらについては 別途記事にしている ので、そちらを参照されたい。

PHPer チャレンジ

- 1位 になった。 また、賞品として Echo Show 15 をいただいた。 + 1位 になった。また、賞品として Echo Show 15 をいただいた。

@@ -197,17 +197,17 @@

- 1つ個人的な反省点としては、(中略) Discord しかりアンカンファレンスしかり「このイベントのこの瞬間にしかないコンテンツ」に触れずに、 後から見返せる発表やスライドに注力してしまった、ということだ。 発表の詳細な見直しはあとからできるのだから、今しかできないことを考えるべきだった。 まあ初カンファレンスだし、とお茶を濁しておこう。 + 1つ個人的な反省点としては、(中略) Discord しかりアンカンファレンスしかり「このイベントのこの瞬間にしかないコンテンツ」に触れずに、後から見返せる発表やスライドに注力してしまった、ということだ。発表の詳細な見直しはあとからできるのだから、今しかできないことを考えるべきだった。まあ初カンファレンスだし、とお茶を濁しておこう。

- この反省を踏まえ、今年は積極的にほかの場 (公式の Discord サーバや、アンカンファレンス) にも参加した。 これにより、参加体験の質がはるかに向上した。特に Discord に関しては、登壇者ご本人による補足や、 質問への回答などがおこなわれる (ことが多い) ため、特別な理由のない限り、発言はしないまでも参加はしておいたほうが良いと思われる。 + この反省を踏まえ、今年は積極的にほかの場 (公式の Discord サーバや、アンカンファレンス) にも参加した。これにより、参加体験の質がはるかに向上した。特に Discord に関しては、登壇者ご本人による補足や、質問への回答などがおこなわれる (ことが多い) ため、特別な理由のない限り、発言はしないまでも参加はしておいたほうが良いと思われる。

なお、アンカンファレンスについては、1日目の終わりに トークン問題の解説放送 もおこなった。

- また、今年はオフラインとオンラインのハイブリッド開催であったが、去年の全オンラインと比べて、オンライン参加の体験が落ちていなかったのは、特筆すべきであろう。 今年は 3回目のワクチン接種が間に合わなかったこともあり現地参加は見送ったのだが、来年は是非オフラインで参加したい。 + また、今年はオフラインとオンラインのハイブリッド開催であったが、去年の全オンラインと比べて、オンライン参加の体験が落ちていなかったのは、特筆すべきであろう。今年は 3回目のワクチン接種が間に合わなかったこともあり現地参加は見送ったのだが、来年は是非オフラインで参加したい。

diff --git a/vhosts/blog/public/posts/2022-08-27/php-conference-okinawa-code-golf/index.html b/vhosts/blog/public/posts/2022-08-27/php-conference-okinawa-code-golf/index.html index ac06818f..9c4eb273 100644 --- a/vhosts/blog/public/posts/2022-08-27/php-conference-okinawa-code-golf/index.html +++ b/vhosts/blog/public/posts/2022-08-27/php-conference-okinawa-code-golf/index.html @@ -134,22 +134,22 @@

指数表記

- 割と多くの言語のゴルフで使えるテクニック。 e を用いた指数表記で、大きな数を短く表す。 このコードでは 10000500020001000 を指数表記している。 + 割と多くの言語のゴルフで使えるテクニック。e を用いた指数表記で、大きな数を短く表す。このコードでは 10000500020001000 を指数表記している。

foreach や for の中身を1つの文に

- foreachforif などの後ろには、 通常 { を続けて複数の文を連ねるが、中身の文を1つにしてしまえば、{} を省略できる。 C言語などでも使える。 + foreachforif などの後ろには、通常 { を続けて複数の文を連ねるが、中身の文を1つにしてしまえば、{} を省略できる。C言語などでも使える。

$r に初期値を入れない

- PHP では、$r[] = ...... のような配列の末尾に追加する式を実行したとき、 $r が未定義だった場合は $r を勝手に定義して空の配列で初期化してくれる。 これを利用すると、$r = []; のような初期化が不要になる。 + PHP では、$r[] = ...... のような配列の末尾に追加する式を実行したとき、$r が未定義だった場合は $r を勝手に定義して空の配列で初期化してくれる。これを利用すると、$r = []; のような初期化が不要になる。

- ただし、プログラムに 0 が渡されるとループを一度も回らないので、$r が未定義になってしまい、 implode() に渡すところでエラーになる。 それを防ぐために $r ?? [] を使っている。 + ただし、プログラムに 0 が渡されるとループを一度も回らないので、$r が未定義になってしまい、implode() に渡すところでエラーになる。それを防ぐために $r ?? [] を使っている。

もし 0 が渡されたケースを無視するなら、これが不要になるので 4 バイト縮む。 @@ -158,7 +158,7 @@

PHP タグの外に文字列を置く

- PHP では、<?php ?> で囲われた部分の外側にある文字列は、そのまま出力される。 今回のケースでは、先頭と末尾に必ず [] を出力するので、そのまま書いてやればよい。 + PHP では、<?php ?> で囲われた部分の外側にある文字列は、そのまま出力される。今回のケースでは、先頭と末尾に必ず [] を出力するので、そのまま書いてやればよい。

diff --git a/vhosts/blog/public/posts/2022-08-31/support-for-communty-is-employee-benefits/index.html b/vhosts/blog/public/posts/2022-08-31/support-for-communty-is-employee-benefits/index.html index d0c8d9c3..bc097fa3 100644 --- a/vhosts/blog/public/posts/2022-08-31/support-for-communty-is-employee-benefits/index.html +++ b/vhosts/blog/public/posts/2022-08-31/support-for-communty-is-employee-benefits/index.html @@ -76,7 +76,7 @@

- 結局これを通したい (私の中での) 最大の理由が、「自分の勤める会社が、これをやる会社であってほしい」というのがあり、 ↑にしても、感情ベースの理由しか出せていないというのが説得力に欠けている理由なのだと思いますが、 寄付の報告が流れてきたり、OSS のフリーライドの話が流れてきたりするたびに、自尊心が毀損される、というか (これは大袈裟すぎる表現で、実際にはそこまで明確に傷ついているわけではありませんが)。 + 結局これを通したい (私の中での) 最大の理由が、「自分の勤める会社が、これをやる会社であってほしい」というのがあり、↑にしても、感情ベースの理由しか出せていないというのが説得力に欠けている理由なのだと思いますが、寄付の報告が流れてきたり、OSS のフリーライドの話が流れてきたりするたびに、自尊心が毀損される、というか (これは大袈裟すぎる表現で、実際にはそこまで明確に傷ついているわけではありませんが)。

追記: 「肩身が狭くなる」というのがより適切でした。 @@ -86,7 +86,7 @@ ※文中の「↑にしても」は、ここに載せていない別の投稿を指しています。

- OSS を金銭的に支援したり、技術カンファレンスへ協賛したり (あるいは CTO がカンファレンスを年2で主催したり: iOSDC PHPerKaigi ) といった行為は、コミュニティへの貢献であると同時に、社員に対する精神的福利厚生でもあると言えるでしょう (知らんけど)。 これらは、技術や技術者を大切にする組織である、ということの、対外的にも対内的にも強力なメッセージなのです。 + OSS を金銭的に支援したり、技術カンファレンスへ協賛したり (あるいは CTO がカンファレンスを年2で主催したり: iOSDC PHPerKaigi ) といった行為は、コミュニティへの貢献であると同時に、社員に対する精神的福利厚生でもあると言えるでしょう (知らんけど)。これらは、技術や技術者を大切にする組織である、ということの、対外的にも対内的にも強力なメッセージなのです。

以上が、私が社内で寄付の件を進めた (かなり私的な) 理由です。 @@ -95,7 +95,7 @@

おわりに

- 最終的に社としての寄付まで漕ぎ着けられたのは、もちろん私の力ではなく役員の方々の決定によるものです。 この場を借りて感謝申し上げます。 + 最終的に社としての寄付まで漕ぎ着けられたのは、もちろん私の力ではなく役員の方々の決定によるものです。この場を借りて感謝申し上げます。

diff --git a/vhosts/blog/public/posts/2022-09-29/write-fizzbuzz-in-php-2-letters-per-line/index.html b/vhosts/blog/public/posts/2022-09-29/write-fizzbuzz-in-php-2-letters-per-line/index.html index 4f1f41ef..2f471ec7 100644 --- a/vhosts/blog/public/posts/2022-09-29/write-fizzbuzz-in-php-2-letters-per-line/index.html +++ b/vhosts/blog/public/posts/2022-09-29/write-fizzbuzz-in-php-2-letters-per-line/index.html @@ -63,7 +63,7 @@

記事の構成について

- この記事は、普通の fizzbuzz を徐々に変形して最終形にしていく、という構成で書かれている。 最終形を見てどのような仕組みで動いているのか解読してから解説を読みたい、というかたがいれば、 このページ にソースコードがあるので、そちらを先に見てほしい。 + この記事は、普通の fizzbuzz を徐々に変形して最終形にしていく、という構成で書かれている。最終形を見てどのような仕組みで動いているのか解読してから解説を読みたい、というかたがいれば、このページ にソースコードがあるので、そちらを先に見てほしい。

@@ -102,7 +102,7 @@

- 備考: PHP には short_open_tag というオプションがあり、 これを有効にするとファイル冒頭の <?php の代わりに <? を使うことができ、文字どおり1行2文字で書ける。 ただ、このオプションはデフォルト off になっている環境が多いようなので、今回は使わないことにした。 + 備考: PHP には short_open_tag というオプションがあり、これを有効にするとファイル冒頭の <?php の代わりに <? を使うことができ、文字どおり1行2文字で書ける。ただ、このオプションはデフォルト off になっている環境が多いようなので、今回は使わないことにした。

@@ -193,7 +193,7 @@ バックスラッシュを使った行継続がトークンを区切らない、というのがポイントだ。

- さて、PHP ではそもそもバックスラッシュを行継続に使うことができない。 これにより、「3文字以上からなるトークンが一切使えない」という制約が課される。 例えば、echo で出力することや、for でループすること、 new でインスタンスを生成することができない。 特に、出力は fizzbuzz をどんなアルゴリズムで実装しようとおこなわなければならないので、できないのは致命的である。 + さて、PHP ではそもそもバックスラッシュを行継続に使うことができない。これにより、「3文字以上からなるトークンが一切使えない」という制約が課される。例えば、echo で出力することや、for でループすること、new でインスタンスを生成することができない。特に、出力は fizzbuzz をどんなアルゴリズムで実装しようとおこなわなければならないので、できないのは致命的である。

当然、名前が3文字以上ある関数も使えない。なお、標準 PHP の範囲内において、名前が 2文字以下の関数は以下のとおりである: @@ -226,7 +226,7 @@ に反する (というより、「それだとおもしろくもなんともないので、このルールを足した」というのが正しい)。

- また、2文字だと文字列がまともに書けないのも辛い。'' だけで2文字使うので、 「1文字の文字列リテラル」というものを書くことができない。PHP では文字列リテラル中に生の改行が書けるので + また、2文字だと文字列がまともに書けないのも辛い。'' だけで2文字使うので、「1文字の文字列リテラル」というものを書くことができない。PHP では文字列リテラル中に生の改行が書けるので

$a
@@ -262,7 +262,7 @@
             

for の排除

- for は、3文字もある長いキーワードである。 こんなものは使えない。array_ 系の関数を使って、適当に置き換えるとしよう。 + for は、3文字もある長いキーワードである。こんなものは使えない。array_ 系の関数を使って、適当に置き換えるとしよう。

<?php
@@ -275,13 +275,13 @@
 );

- array_walkrangeprintf といった for よりも長いトークンが現れてしまったが、これは次節で直すことにする。 なお、echo は文 (statement) であり式 (expression) ではないので、式である printf に置き換えた。 + array_walkrangeprintf といった for よりも長いトークンが現れてしまったが、これは次節で直すことにする。なお、echo は文 (statement) であり式 (expression) ではないので、式である printf に置き換えた。

関数呼び出しの短縮

- rangearray_walkprintf は長すぎるのでどうにかせねばならない。 ここで、PHP の可変関数を使う。可変関数とは、関数名が文字列として入った変数を経由して、関数を呼び出す機能である。 + rangearray_walkprintf は長すぎるのでどうにかせねばならない。ここで、PHP の可変関数を使う。可変関数とは、関数名が文字列として入った変数を経由して、関数を呼び出す機能である。

<?php
@@ -298,7 +298,7 @@
 );

- これで関数を呼び出している所は短くなった。 では、$r$w$p、 また 'Fizz''Buzz' はどうやって 1 行 2 文字に収めるのか。 次のテクニックへ移ろう。 + これで関数を呼び出している所は短くなった。では、$r$w$p、また 'Fizz''Buzz' はどうやって 1 行 2 文字に収めるのか。次のテクニックへ移ろう。

@@ -314,7 +314,7 @@

- というルールがない場合、「未定義の定数が評価された場合、その定数の名前が値になる」という PHP 7.x までの仕様が利用できる。 例えば、 Fizz という文字列が欲しければ、次のようにする。 + というルールがない場合、「未定義の定数が評価された場合、その定数の名前が値になる」という PHP 7.x までの仕様が利用できる。例えば、 Fizz という文字列が欲しければ、次のようにする。

$f
@@ -325,7 +325,7 @@
 ;;

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

$f
@@ -348,7 +348,7 @@
                 実際に使った手法の説明に移る。
               

- ずばり、文字列同士のビット演算を使う。 PHP では、文字列同士でビット演算 (&|^) をした場合、 文字列の各バイトごとに指定したビット演算がなされ、それを結合したものが演算結果となる。 + ずばり、文字列同士のビット演算を使う。PHP では、文字列同士でビット演算 (&|^) をした場合、文字列の各バイトごとに指定したビット演算がなされ、それを結合したものが演算結果となる。

$a = "12345";
@@ -373,7 +373,7 @@
 echo "$r\n";

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

$x
@@ -413,10 +413,10 @@
 echo "$r\n";

- 1行あたり2文字で、range という文字列を生成することに成功した。 他の必要な文字列にも、同様の処理をほどこす。 + 1行あたり2文字で、range という文字列を生成することに成功した。他の必要な文字列にも、同様の処理をほどこす。

- 備考: Buzz 中にある小文字の u は、このロジックだと non-printable な文字になってしまう。 ここまでのテクニックを駆使すれば回避するのはそう難しくないので、考えてみてほしい。 + 備考: Buzz 中にある小文字の u は、このロジックだと non-printable な文字になってしまう。ここまでのテクニックを駆使すれば回避するのはそう難しくないので、考えてみてほしい。

@@ -580,7 +580,7 @@

感想など

- PHP は、スクリプト言語の中だとシンタックスシュガーが少ない (体感)。 この挑戦は不可能に思われたが、PHP マニュアルとにらめっこしていたらなんとかなった。 + PHP は、スクリプト言語の中だとシンタックスシュガーが少ない (体感)。この挑戦は不可能に思われたが、PHP マニュアルとにらめっこしていたらなんとかなった。

みんなもプログラムを細長くしよう。 @@ -589,7 +589,7 @@

余談2: 別解

- PHP では、バッククォートを使ってシェルを呼び出せる。 これは shell_exec 関数と等価である。 さて、PHP ではバックスラッシュによる行継続が使えないと書いたが、シェルでは使える (当然だが、呼び出されるシェルに依存する。Bash なら大丈夫だろう。知らんけど)。 + PHP では、バッククォートを使ってシェルを呼び出せる。これは shell_exec 関数と等価である。さて、PHP ではバックスラッシュによる行継続が使えないと書いたが、シェルでは使える (当然だが、呼び出されるシェルに依存する。Bash なら大丈夫だろう。知らんけど)。

<?php
@@ -606,7 +606,7 @@
 `);

- なお、ここでは簡単のため出力に printf をそのまま使っているが、 実際には printf という文字列を合成して可変関数で呼び出す。 + なお、ここでは簡単のため出力に printf をそのまま使っているが、実際には printf という文字列を合成して可変関数で呼び出す。

ただし、これでは @@ -664,7 +664,7 @@ '}

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

e\
@@ -677,7 +677,7 @@
 3\

- これは、前掲したコマンドと同じだ。 かくして、スペースを陽に書かずにシェルをおおよそ自由に扱えるようになった。 Fizzbuzz のワンライナーくらいすぐ書けるだろうから、あとはなんとかなるだろう (試してないけど)。 + これは、前掲したコマンドと同じだ。かくして、スペースを陽に書かずにシェルをおおよそ自由に扱えるようになった。Fizzbuzz のワンライナーくらいすぐ書けるだろうから、あとはなんとかなるだろう (試してないけど)。

ということでこれは別解ということにしておく。 diff --git a/vhosts/blog/public/posts/2022-10-23/phperkaigi-2023-unused-token-quiz-1/index.html b/vhosts/blog/public/posts/2022-10-23/phperkaigi-2023-unused-token-quiz-1/index.html index 971f3c28..270ca0c2 100644 --- a/vhosts/blog/public/posts/2022-10-23/phperkaigi-2023-unused-token-quiz-1/index.html +++ b/vhosts/blog/public/posts/2022-10-23/phperkaigi-2023-unused-token-quiz-1/index.html @@ -63,13 +63,13 @@

はじめに

- 2023 年 3 月 23 日から 25 日にかけて開催予定 (記事執筆時点) の、 PHPerKaigi 2023 において、 昨年と同様に、弊社 デジタルサーカス株式会社 から、 トークン問題を出題予定である。 + 2023 年 3 月 23 日から 25 日にかけて開催予定 (記事執筆時点) の、PHPerKaigi 2023 において、昨年と同様に、弊社 デジタルサーカス株式会社 から、トークン問題を出題予定である。

昨年のトークン問題の記事はこちら: PHPerKaigi 2022 トークン問題の解説

- すでに 2023 年用の問題は作成済みであるが、その制作過程の中でいくつかボツ問ができた。 せっかくなので、PHPerKaigi 開催を待つ間に紹介しようと思う。 + すでに 2023 年用の問題は作成済みであるが、その制作過程の中でいくつかボツ問ができた。せっかくなので、PHPerKaigi 開催を待つ間に紹介しようと思う。

10 月から 2 月まで、毎月 1 記事ずつ公開していく予定 (忘れていなければ)。 @@ -107,7 +107,7 @@

トークン入手方法

- ソースを見るとわかるとおり、$argv[1] を参照している。 それを なる変数に代入しているので、円周率を渡してみる。 + ソースを見るとわかるとおり、$argv[1] を参照している。それを なる変数に代入しているので、円周率を渡してみる。

$ php Q.php 3.14
@@ -156,10 +156,10 @@
               
$s = implode(array_map(chr(...), str_split($π, 2)));

- を 2 文字ごとに区切り (str_split)、 数値を ASCII コードと見做して文字に変換 (chr) して結合 (implode) している。 + を 2 文字ごとに区切り (str_split)、数値を ASCII コードと見做して文字に変換 (chr) して結合 (implode) している。

- 例えば、'656667' だったとすると、 656667 に対応した 'A''B''C' へと変換され、'ABC' になる。 + 例えば、'656667' だったとすると、656667 に対応した 'A''B''C' へと変換され、'ABC' になる。

= '656667';
@@ -172,10 +172,10 @@
 $t = $m[1] ?? '';

- 正規表現でマッチングしている。\x23# と同じであることに留意すると、 この正規表現は「# から始まる 2 以上の長さ (含 #) の文字列で、 最初に現れるスペースまで」にマッチする。つまりこれは、PHPerKaigi におけるトークンである。 + 正規表現でマッチングしている。\x23# と同じであることに留意すると、この正規表現は「# から始まる 2 以上の長さ (含 #) の文字列で、最初に現れるスペースまで」にマッチする。つまりこれは、PHPerKaigi におけるトークンである。

- なお、# を直接書いていないのは、/#.+?) / と書くと、 #.+?) という意図せぬトークンが登録されてしまうからである。 + なお、# を直接書いていないのは、/#.+?) / と書くと、#.+?) という意図せぬトークンが登録されてしまうからである。

if (md5($t) === '056e831a4146bf123e8ea16613303d2e') {
@@ -194,7 +194,7 @@
               円周率を何桁も計算して ASCII コード経由で文字列化すれば、トークンっぽいものがどこかで出てくるのではないか、と考えて生まれた作品。
             

- 最初は真面目に円周率の計算プログラムを組んでいたのだが、いざ動かしてみるとやけに浅いところにあったので驚いた (ちなみに、それでも M_PIpi() では精度が足りない)。 見つけたときは狂喜したものの、冷静になってみると大して面白くなかったのでボツになった。 むしろ、100 万桁目くらいに埋まっていてくれたほうがよかったかもしれない。 + 最初は真面目に円周率の計算プログラムを組んでいたのだが、いざ動かしてみるとやけに浅いところにあったので驚いた (ちなみに、それでも M_PIpi() では精度が足りない)。見つけたときは狂喜したものの、冷静になってみると大して面白くなかったのでボツになった。むしろ、100 万桁目くらいに埋まっていてくれたほうがよかったかもしれない。

diff --git a/vhosts/blog/public/posts/2022-10-28/setup-server-for-this-site/index.html b/vhosts/blog/public/posts/2022-10-28/setup-server-for-this-site/index.html index ee037dee..95b2d08d 100644 --- a/vhosts/blog/public/posts/2022-10-28/setup-server-for-this-site/index.html +++ b/vhosts/blog/public/posts/2022-10-28/setup-server-for-this-site/index.html @@ -63,7 +63,7 @@

はじめに

- これまでこの blog は GitHub Pages でホストしていたのだが、先日 VPS に移行した。 そのときにおこなったサーバのセットアップ作業を書き残しておく。 99 % 自分用の備忘録。別のベンダに移したりしたくなったら見に来る。 + これまでこの blog は GitHub Pages でホストしていたのだが、先日 VPS に移行した。そのときにおこなったサーバのセットアップ作業を書き残しておく。99 % 自分用の備忘録。別のベンダに移したりしたくなったら見に来る。

未来の自分へ: 特に自動化してないので、せいぜい苦しんでくれ。 @@ -72,7 +72,7 @@

VPS

- さくらの VPS の 2 GB プラン。 そこまで真面目に選定していないので、困ったら移動するかも。 + さくらの VPS の 2 GB プラン。そこまで真面目に選定していないので、困ったら移動するかも。

@@ -80,7 +80,7 @@

サーバのホスト名を決める

- モチベーションが上がるという効能がある。今回は藤原定家から取って teika にした。 たいていいつも源氏物語の帖か小倉百人一首の歌人から選んでいる。 + モチベーションが上がるという効能がある。今回は藤原定家から取って teika にした。たいていいつも源氏物語の帖か小倉百人一首の歌人から選んでいる。

@@ -93,7 +93,7 @@ $ ssh-keygen -t ed25519 -b 521 -f ~/.ssh/github2teika.key

- teika.key はローカルからサーバへの接続用、github2teika.key は、 GitHub Actions からサーバへのデプロイ用。 + teika.key はローカルからサーバへの接続用、github2teika.key は、GitHub Actions からサーバへのデプロイ用。

@@ -122,7 +122,7 @@

ユーザを作成する

- 管理者ユーザで作業すると危ないので、メインで使うユーザを作成する。 sudo グループに追加して sudo できるようにし、su で切り替え。 + 管理者ユーザで作業すると危ないので、メインで使うユーザを作成する。sudo グループに追加して sudo できるようにし、su で切り替え。

$ sudo adduser **********
@@ -179,7 +179,7 @@
             

SSH で接続確認

- 今の SSH セッションは閉じずに、ターミナルを別途開いて疎通確認する。 セッションを閉じてしまうと、SSH の設定に不備があった場合に締め出しをくらう。 + 今の SSH セッションは閉じずに、ターミナルを別途開いて疎通確認する。セッションを閉じてしまうと、SSH の設定に不備があった場合に締め出しをくらう。

$ ssh teika
@@ -303,7 +303,7 @@

感想

- (業務でなく) 個人だと数年ぶりのサーバセットアップで、これだけでも割と時間を食ってしまった。 とはいえ式年遷宮は楽しいので、これからも定期的にやっていきたい。 コンテナデプロイにしたい気持ちもあるのだが、色々実験したい関係上、本物のサーバも欲しくはある。 次の式年遷宮では、手順の一部だけでも自動化したいところ。 + (業務でなく) 個人だと数年ぶりのサーバセットアップで、これだけでも割と時間を食ってしまった。とはいえ式年遷宮は楽しいので、これからも定期的にやっていきたい。コンテナデプロイにしたい気持ちもあるのだが、色々実験したい関係上、本物のサーバも欲しくはある。次の式年遷宮では、手順の一部だけでも自動化したいところ。

diff --git a/vhosts/blog/public/posts/2022-11-19/phperkaigi-2023-unused-token-quiz-2/index.html b/vhosts/blog/public/posts/2022-11-19/phperkaigi-2023-unused-token-quiz-2/index.html index 5f1adda5..5a97f3f7 100644 --- a/vhosts/blog/public/posts/2022-11-19/phperkaigi-2023-unused-token-quiz-2/index.html +++ b/vhosts/blog/public/posts/2022-11-19/phperkaigi-2023-unused-token-quiz-2/index.html @@ -63,7 +63,7 @@

はじめに

- 2023 年 3 月 23 日から 25 日にかけて開催予定 (記事執筆時点) の PHPerKaigi 2023 において、 昨年と同様に、弊社 デジタルサーカス株式会社 からトークン問題を出題予定である。 + 2023 年 3 月 23 日から 25 日にかけて開催予定 (記事執筆時点) の PHPerKaigi 2023 において、昨年と同様に、弊社 デジタルサーカス株式会社 からトークン問題を出題予定である。

昨年のトークン問題の記事はこちら: PHPerKaigi 2022 トークン問題の解説 diff --git a/vhosts/blog/public/posts/2023-01-10/phperkaigi-2023-unused-token-quiz-3/index.html b/vhosts/blog/public/posts/2023-01-10/phperkaigi-2023-unused-token-quiz-3/index.html index 94a99322..48d04436 100644 --- a/vhosts/blog/public/posts/2023-01-10/phperkaigi-2023-unused-token-quiz-3/index.html +++ b/vhosts/blog/public/posts/2023-01-10/phperkaigi-2023-unused-token-quiz-3/index.html @@ -63,13 +63,13 @@

はじめに

- 2023 年 3 月 23 日から 25 日にかけて開催予定 (記事執筆時点) の PHPerKaigi 2023 において、 昨年と同様に、弊社 デジタルサーカス株式会社 からトークン問題を出題予定である。 + 2023 年 3 月 23 日から 25 日にかけて開催予定 (記事執筆時点) の PHPerKaigi 2023 において、昨年と同様に、弊社 デジタルサーカス株式会社 からトークン問題を出題予定である。

昨年のトークン問題の記事はこちら: PHPerKaigi 2022 トークン問題の解説

- すでに 2023 年用の問題は作成済みであるが、その制作過程の中でいくつかボツ問ができた。 せっかくなので、PHPerKaigi 開催を待つ間に紹介しようと思う。 + すでに 2023 年用の問題は作成済みであるが、その制作過程の中でいくつかボツ問ができた。せっかくなので、PHPerKaigi 開催を待つ間に紹介しようと思う。

10 月から 2 月まで、毎月 1 記事ずつ公開していく予定 (忘れていなければ → 忘れていたので 12 月公開予定だった記事を今書いている)。 diff --git a/vhosts/blog/public/posts/2023-03-10/rewrite-this-blog-generator/index.html b/vhosts/blog/public/posts/2023-03-10/rewrite-this-blog-generator/index.html index 4443d31d..a0e6c642 100644 --- a/vhosts/blog/public/posts/2023-03-10/rewrite-this-blog-generator/index.html +++ b/vhosts/blog/public/posts/2023-03-10/rewrite-this-blog-generator/index.html @@ -54,7 +54,7 @@

はじめに

- このブログを構築するシステムを書き直したのは 2度目である。 元々立ち上げた当初は、静的サイトジェネレータである Hugo を使っていた。 それを Asciidoctor にいくつかのカスタムを加えた自前のジェネレータに移行したのが 2022年の11月ごろだ。 そして今回、スクラッチから書いた Deno 製のジェネレータに移行した。 + このブログを構築するシステムを書き直したのは 2度目である。元々立ち上げた当初は、静的サイトジェネレータである Hugo を使っていた。それを Asciidoctor にいくつかのカスタムを加えた自前のジェネレータに移行したのが 2022年の11月ごろだ。そして今回、スクラッチから書いた Deno 製のジェネレータに移行した。

この記事では、移行の理由などを (主に将来の私へ向けて) 書き記しておく。 @@ -63,7 +63,7 @@

Hugo から Asciidoctor へ

- 最初に断っておくと、Hugo は大変に優れた静的サイトジェネレータである。移行の理由の大半は、自分でジェネレータを書きたかったからに他ならない。 実のところ、この記事を執筆している現在、自作ジェネレータは Hugo よりも機能が劣っている。 例えば、Hugo を使っていたころはサポートしていた RSS フィードの生成は、まだ実装できていない。 + 最初に断っておくと、Hugo は大変に優れた静的サイトジェネレータである。移行の理由の大半は、自分でジェネレータを書きたかったからに他ならない。実のところ、この記事を執筆している現在、自作ジェネレータは Hugo よりも機能が劣っている。例えば、Hugo を使っていたころはサポートしていた RSS フィードの生成は、まだ実装できていない。

移行先のフォーマットとして AsciiDoc を選んだのは、Markdown よりも表現力に優れるからである。Markdown は広く使われている軽量マークアップ言語だが、以下のような欠点を持つ。 @@ -117,7 +117,7 @@ フォーマットが求められた。これに合致したのが、XML をベースとする DocBook (今回使っているのは、そのサブセットである Simplified DocBook ) である。

- 実は、AsciiDoc と DocBook はおおよそ互換性がある。AsciiDoc で書かれた文書は (ほぼ) 情報ロスなしに DocBook へ変換でき、逆もまたしかりである。 よって、DocBook には、AsciiDoc と同等の表現力がある。 + 実は、AsciiDoc と DocBook はおおよそ互換性がある。AsciiDoc で書かれた文書は (ほぼ) 情報ロスなしに DocBook へ変換でき、逆もまたしかりである。よって、DocBook には、AsciiDoc と同等の表現力がある。

XML の文法の厳密さについては、説明するまでもないだろう。また、単純な文法であることから実装が容易であり、事実上 Asciidoctor へロックインされる AsciiDoc とは異なり、さまざまな言語で多くのライブラリが存在する。 @@ -126,16 +126,16 @@ 今回は、XML のパース自体も自分で書いている (これは何となく書きたかったからであり、合理的な理由があるわけではない。実装はサボりまくっているので XML のコメントが使えないといった制限がある)。

- XML という機械処理しやすいフォーマットを選ぶことには、機械的な変換や検査といった処理がおこないやすくなるといった利点もある。 欠点は軽量マークアップ言語と比べて冗長であることだが、書く際は補完などを用いるのでそれほど気にならない。 結局のところ、技術ブログの執筆を律速するのは調査と文章の記述であり、マークアップの手段は執筆時間に大した影響を与えない。 + XML という機械処理しやすいフォーマットを選ぶことには、機械的な変換や検査といった処理がおこないやすくなるといった利点もある。欠点は軽量マークアップ言語と比べて冗長であることだが、書く際は補完などを用いるのでそれほど気にならない。結局のところ、技術ブログの執筆を律速するのは調査と文章の記述であり、マークアップの手段は執筆時間に大した影響を与えない。

おわりに

- 2度のリライトを経て、記事のフォーマットとサイトジェネレータを上から下まで掌握した。 今後も改善のアイデアは多数あるので、じわじわと進めていきたいところだ。 + 2度のリライトを経て、記事のフォーマットとサイトジェネレータを上から下まで掌握した。今後も改善のアイデアは多数あるので、じわじわと進めていきたいところだ。

- 最後にもう一度書くのだが、Hugo は大変に優れた静的サイトジェネレータである。 無駄な拘りがなければこれを使うとよい。 私は無駄に拘ったので、ブログの記事を書く時間を潰してブログシステムを作ってしまった。 + 最後にもう一度書くのだが、Hugo は大変に優れた静的サイトジェネレータである。無駄な拘りがなければこれを使うとよい。私は無駄に拘ったので、ブログの記事を書く時間を潰してブログシステムを作ってしまった。

diff --git a/vhosts/blog/public/posts/2023-04-01/implementation-of-minimal-png-image-encoder/index.html b/vhosts/blog/public/posts/2023-04-01/implementation-of-minimal-png-image-encoder/index.html index 22af315a..ff7e58c7 100644 --- a/vhosts/blog/public/posts/2023-04-01/implementation-of-minimal-png-image-encoder/index.html +++ b/vhosts/blog/public/posts/2023-04-01/implementation-of-minimal-png-image-encoder/index.html @@ -54,7 +54,7 @@

はじめに

- この記事では、PNG 画像として valid な範囲で最大限手抜きしたエンコーダを書く。 PNG 画像に対応したビューアであれば読み込めるが、圧縮効率については一切考えない。 また、実装には Go 言語を使うが、Go の標準ライブラリにあるさまざまなアルゴリズム (PNG 画像に関係する範囲だと、zlib や CRC32、Adler-32 など) は使わない。 + この記事では、PNG 画像として valid な範囲で最大限手抜きしたエンコーダを書く。PNG 画像に対応したビューアであれば読み込めるが、圧縮効率については一切考えない。また、実装には Go 言語を使うが、Go の標準ライブラリにあるさまざまなアルゴリズム (PNG 画像に関係する範囲だと、zlib や CRC32、Adler-32 など) は使わない。

@@ -77,7 +77,7 @@

- Chunk には画像データを入れる IDAT chunk、パレットデータを入れる PLTE chunk、テキストデータを入れる tEXt chunk などがあるが、 今回は最小構成ということで IDAT chunk (と IHDR chunk と IEND chunk) のみを用いる。 + Chunk には画像データを入れる IDAT chunk、パレットデータを入れる PLTE chunk、テキストデータを入れる tEXt chunk などがあるが、今回は最小構成ということで IDAT chunk (と IHDR chunk と IEND chunk) のみを用いる。

次節で、それぞれの具体的な構造を確認しつつ実装していく。 @@ -86,7 +86,7 @@

PNG のエンコーダを実装する

- 以下のソースコードをベースにする。 今回 PNG のデコーダは扱わないので、読み込みには Go の標準ライブラリ image/png を用いる。 + 以下のソースコードをベースにする。今回 PNG のデコーダは扱わないので、読み込みには Go の標準ライブラリ image/png を用いる。

package main
@@ -263,7 +263,7 @@
 }

- 仕様どおり、chunkTypedata から CRC を計算し、data の長さと合わせて書き込んでいる。 PNG では基本的に big endian を使うことに注意する。 + 仕様どおり、chunkTypedata から CRC を計算し、data の長さと合わせて書き込んでいる。PNG では基本的に big endian を使うことに注意する。

準備ができたところで、具体的な chunk をエンコードしていく。 @@ -455,10 +455,10 @@

画像データ

- では次に、zlib 形式で格納するデータを用意する。PNG 画像は次のような順にスキャンする。 画像の左上のピクセルから同じ行を横にスキャンしていき、一番右まで到達したら次の行の左に向かう。 右下のピクセルまで行けば終わり。要は Z 字型に進んでいく。 + では次に、zlib 形式で格納するデータを用意する。PNG 画像は次のような順にスキャンする。画像の左上のピクセルから同じ行を横にスキャンしていき、一番右まで到達したら次の行の左に向かう。右下のピクセルまで行けば終わり。要は Z 字型に進んでいく。

- また、それぞれの行の先頭には、圧縮のためのフィルタタイプを指定する。 ただ、今回はその実装を省略するために、常にフィルタ 0 (何も加工しない) を使う。 + また、それぞれの行の先頭には、圧縮のためのフィルタタイプを指定する。ただ、今回はその実装を省略するために、常にフィルタ 0 (何も加工しない) を使う。

先ほどの encodeZlib も使って実際に実装したものがこちら: diff --git a/vhosts/blog/public/posts/2023-04-04/phperkaigi-2023-report/index.html b/vhosts/blog/public/posts/2023-04-04/phperkaigi-2023-report/index.html index b2a5bcd8..da788f05 100644 --- a/vhosts/blog/public/posts/2023-04-04/phperkaigi-2023-report/index.html +++ b/vhosts/blog/public/posts/2023-04-04/phperkaigi-2023-report/index.html @@ -69,7 +69,7 @@

はじめに

- 2023-03-23 から 2023-03-25 にかけて開催された、 PHPerKaigi 2023 に参加した。 今年は 2つのセッションのスピーカーとして、また、当日スタッフとして参加した。 + 2023-03-23 から 2023-03-25 にかけて開催された、 PHPerKaigi 2023 に参加した。今年は 2つのセッションのスピーカーとして、また、当日スタッフとして参加した。

昨年、一昨年の参加レポはこちら: @@ -119,16 +119,16 @@

- PHPer チャレンジの話については後述する。 参照については、PHP を書き始めた頃からずっと疑問に思っていたので、仕組みを理解する良い機会となった。 + PHPer チャレンジの話については後述する。参照については、PHP を書き始めた頃からずっと疑問に思っていたので、仕組みを理解する良い機会となった。

当日スタッフとして

- 今回はスピーカーのみならず当日スタッフとしても参加した。 カンファレンスのスタッフとしての参加は初めてだったが、初参加のスタッフでもスムーズに作業ができるような仕組みが整えられていた。 + 今回はスピーカーのみならず当日スタッフとしても参加した。カンファレンスのスタッフとしての参加は初めてだったが、初参加のスタッフでもスムーズに作業ができるような仕組みが整えられていた。

- PHPerKaigi は一般参加者の目線でもよくできたカンファレンスだなあという印象だったのだが、よりその思いを強くした。 なんとスタッフにとってもよくできたカンファレンスなのである。 + PHPerKaigi は一般参加者の目線でもよくできたカンファレンスだなあという印象だったのだが、よりその思いを強くした。なんとスタッフにとってもよくできたカンファレンスなのである。

反省点は私自身の最大 HP がまったく足りていなかったことで、次の機会には最後まで動けるようにしたいところである。 @@ -145,7 +145,7 @@ ブラウザの向こう側で「200 OK」を返すまでに何が起きているのか調べてみた

- Web に関わるなら、バックエンドでもフロントエンドでも知っておいてほしい知識。 タイトルを見て「こんな話だろうな」と想像がつくレベルなら見なくてもいいかも。 + Web に関わるなら、バックエンドでもフロントエンドでも知っておいてほしい知識。タイトルを見て「こんな話だろうな」と想像がつくレベルなら見なくてもいいかも。

PHPで学ぶ “Cacheの距離” の話 @@ -163,22 +163,22 @@ PHPの最高機能、配列を捨てよう!!

- 実はこれも上のセッションと同様の話。 PHP の静的解析ツールは配列にも (無理矢理) 型が付けられるものが多いが、実行時にも検査できるという点において専用のクラスを作る方が優れている。 + 実はこれも上のセッションと同様の話。PHP の静的解析ツールは配列にも (無理矢理) 型が付けられるものが多いが、実行時にも検査できるという点において専用のクラスを作る方が優れている。

時間を気にせず普通にカンニングもしつつ ISUCON12 本選問題を PHP でやってみる

- 個人的に最も楽しみにしていたセッションであり、今回のモリアガリトーク賞 (盛り上がったセッションに運営側から贈られる賞) でもある。 ネタバレになるが、最終的に (Go で実装された) 本戦優勝スコアを超えている。 + 個人的に最も楽しみにしていたセッションであり、今回のモリアガリトーク賞 (盛り上がったセッションに運営側から贈られる賞) でもある。ネタバレになるが、最終的に (Go で実装された) 本戦優勝スコアを超えている。

PHPer チャレンジ

- 昨年に引き続き、弊社デジタルサーカス株式会社からのトークン問題の作題を担当した。 また、今年はさらに作成した問題を解説するセッションにも登壇した。 今年のトークンは、昨年の PHPerKaigi 2022 が終わった段階から作り始め、約半年かけて制作した。 + 昨年に引き続き、弊社デジタルサーカス株式会社からのトークン問題の作題を担当した。また、今年はさらに作成した問題を解説するセッションにも登壇した。今年のトークンは、昨年の PHPerKaigi 2022 が終わった段階から作り始め、約半年かけて制作した。

- 問題の制作中は大変楽しかったが、まあやりすぎた。 いかに超絶技巧を凝らすかに注力してしまい、解く楽しさという観点を失ってしまったきらいがある。 + 問題の制作中は大変楽しかったが、まあやりすぎた。いかに超絶技巧を凝らすかに注力してしまい、解く楽しさという観点を失ってしまったきらいがある。

(WIP: 解説ブログ記事執筆中。終わったらここにリンク) @@ -239,13 +239,13 @@

- プロポーザルに関しては採択されて登壇できたし、PHPer チャレンジは解説もおこなった。また、現地に行くだけでなく、当日スタッフとして参加した。 4つ目の PHPer チャレンジに関しては、今年は参加していない。 スタッフをやりながらだと入力する時間も探す時間も取れそうになかったのと、スタッフをやっている関係で少しだけ早く入手してしまうトークンがいくつか存在していたため。 + プロポーザルに関しては採択されて登壇できたし、PHPer チャレンジは解説もおこなった。また、現地に行くだけでなく、当日スタッフとして参加した。4つ目の PHPer チャレンジに関しては、今年は参加していない。スタッフをやりながらだと入力する時間も探す時間も取れそうになかったのと、スタッフをやっている関係で少しだけ早く入手してしまうトークンがいくつか存在していたため。

カンファレンス全体の感想についてだが、大規模なカンファレンスにオフラインで参加するのは今回が初めてだったので、その話をしたい。

- オンラインとオフラインだと体験が別物になる。そもそもが似て非なるものなのだ。 向き不向きはあるだろうが、オンラインしか参加したことのないという方は、一度現地参加してみてはいかがだろうか。 + オンラインとオフラインだと体験が別物になる。そもそもが似て非なるものなのだ。向き不向きはあるだろうが、オンラインしか参加したことのないという方は、一度現地参加してみてはいかがだろうか。

さて、参加レポは去年も一昨年もこの言葉で締め括っているので、今年もそれで終わろうと思う。 diff --git a/vhosts/blog/public/posts/2023-06-25/phpconfuk-2023-report/index.html b/vhosts/blog/public/posts/2023-06-25/phpconfuk-2023-report/index.html index 86fad30f..cba287cb 100644 --- a/vhosts/blog/public/posts/2023-06-25/phpconfuk-2023-report/index.html +++ b/vhosts/blog/public/posts/2023-06-25/phpconfuk-2023-report/index.html @@ -66,7 +66,7 @@

はじめに

- 2023-06-24 に開催された、 PHP カンファレンス福岡 2023 に参加した。 また、その前日に催された、 非公式の前夜祭 にも参加した。 前夜祭では、15分の登壇もおこなった。 登壇の方の資料はこちら。 + 2023-06-24 に開催された、 PHP カンファレンス福岡 2023 に参加した。また、その前日に催された、 非公式の前夜祭 にも参加した。前夜祭では、15分の登壇もおこなった。 登壇の方の資料はこちら。

@@ -142,7 +142,7 @@

おわりに

- 居住地域から離れた場所への遠征参加は初めてだったが、大変楽しい (しかも勉強にもなる!) 体験だった。 受け取った「熱」が冷める前に、自らの手を動かしていきたい。 + 居住地域から離れた場所への遠征参加は初めてだったが、大変楽しい (しかも勉強にもなる!) 体験だった。受け取った「熱」が冷める前に、自らの手を動かしていきたい。

diff --git a/vhosts/blog/public/posts/2023-10-02/compile-php-runtime-to-wasm/index.html b/vhosts/blog/public/posts/2023-10-02/compile-php-runtime-to-wasm/index.html index c17bc163..bd286a6d 100644 --- a/vhosts/blog/public/posts/2023-10-02/compile-php-runtime-to-wasm/index.html +++ b/vhosts/blog/public/posts/2023-10-02/compile-php-runtime-to-wasm/index.html @@ -101,7 +101,7 @@

本記事のゴール

- 先にこの記事のゴールを示しておく。これから示す手順のとおりに進めると、次のようなコードが動くようになる。 このコードはこのあと使うので、index.mjs の名前で保存しておくこと。 + 先にこの記事のゴールを示しておく。これから示す手順のとおりに進めると、次のようなコードが動くようになる。このコードはこのあと使うので、index.mjs の名前で保存しておくこと。

@@ -161,10 +161,10 @@ ほとんどはただの PHP の公開 API を使ったコードだが、Emscripten 向けの注意点が 2点ある。

- まずは EMSCRIPTEN_KEEPALIVE について。 これは Emscripten が用意している特殊なマクロである。 このマクロが付与されている関数は、どこからも使用されていなくともコンパイル後の WebAssembly バイナリから削除されない。 もしこれを付け忘れると、未使用の関数とみなされ削除される。 + まずは EMSCRIPTEN_KEEPALIVE について。これは Emscripten が用意している特殊なマクロである。このマクロが付与されている関数は、どこからも使用されていなくともコンパイル後の WebAssembly バイナリから削除されない。もしこれを付け忘れると、未使用の関数とみなされ削除される。

- 次に、コードを評価したあとに呼んでいる標準出力と標準エラー出力に対する改行の出力について。 出力バッファから出力させるためだけなら改行を出力させなくとも fflush() だけで事足りると考えたのだが、ないと動かなかったので追加した。 これにより、PHP コードの出力の後ろに余分な改行が追加されてしまう。 改行を出力せずともバッファを消費させる手段をご存知のかたはご教示願いたい。 + 次に、コードを評価したあとに呼んでいる標準出力と標準エラー出力に対する改行の出力について。出力バッファから出力させるためだけなら改行を出力させなくとも fflush() だけで事足りると考えたのだが、ないと動かなかったので追加した。これにより、PHP コードの出力の後ろに余分な改行が追加されてしまう。改行を出力せずともバッファを消費させる手段をご存知のかたはご教示願いたい。

@@ -172,7 +172,7 @@

- fflush() の前に改行の出力が必要だった理由が判明したので追記する。 これは、index.mjs で標準出力・標準エラー出力へ出力する方法を指定せず、デフォルトの実装に任せているため。 Emscripten のデフォルト実装では、改行コードを出力するまで出力内容がバッファリングされ、fflush() が機能しない。 + fflush() の前に改行の出力が必要だった理由が判明したので追記する。これは、index.mjs で標準出力・標準エラー出力へ出力する方法を指定せず、デフォルトの実装に任せているため。Emscripten のデフォルト実装では、改行コードを出力するまで出力内容がバッファリングされ、fflush() が機能しない。

デフォルトの出力方法は index.mjs の中で PHPWasm() を呼ぶとき、stdoutstderr というオプションを渡せば変更できる。 @@ -209,7 +209,7 @@

FROM emscripten/emsdk:3.1.46 AS wasm-builder

- 次に、 php/php-src から PHP 処理系のソースコードを取得し、ビルドに必要な apt パッケージを取ってくる。 有効にする拡張を増やしたいなら、ここでインストールするパッケージも増やすことになるだろう。 + 次に、 php/php-src から PHP 処理系のソースコードを取得し、ビルドに必要な apt パッケージを取ってくる。有効にする拡張を増やしたいなら、ここでインストールするパッケージも増やすことになるだろう。

RUN git clone --depth=1 --branch=php-8.2.10 https://github.com/php/php-src
@@ -254,22 +254,22 @@
                 ここまでと比べると少し複雑なので、それぞれ詳しく見ていこう。
               

- まず、buildconf は PHP 処理系をビルドするときに (Emscripten とは関係なく) 使うツールである。 このツールの最も重要な仕事は、configure の生成である。 + まず、buildconf は PHP 処理系をビルドするときに (Emscripten とは関係なく) 使うツールである。このツールの最も重要な仕事は、configure の生成である。

- 次に configure するわけだが、ここで emconfigure を使う。 これを使うことで、Emscripten が上手く諸々のツールチェインを WebAssembly のビルド向けに調整しながら configure してくれる。 + 次に configure するわけだが、ここで emconfigure を使う。これを使うことで、Emscripten が上手く諸々のツールチェインを WebAssembly のビルド向けに調整しながら configure してくれる。

- configure の後ろに指定してあるフラグは、通常の PHP 処理系のビルドで使う configure と同じなので、詳しくはそちらの cofigure --help を参照していただきたい。 ほとんどは、機能の無効化のために指定している (依存するライブラリを減らし、ビルドをより簡単にするため)。 + configure の後ろに指定してあるフラグは、通常の PHP 処理系のビルドで使う configure と同じなので、詳しくはそちらの cofigure --help を参照していただきたい。ほとんどは、機能の無効化のために指定している (依存するライブラリを減らし、ビルドをより簡単にするため)。

- 通常の C のビルドなら、configure の次は make するところだが、ここでも emmake を使う。 役割はほとんど emconfigure と同様である。 指定してある EMCC_CFLAGS という環境変数は、Emscripten の C コンパイラへのフラグで、ここでは ERROR_ON_UNDEFINED_SYMBOLS を無効化している。 これにより、コンパイル中に出現した解決できなかったシンボルを無視するようになる (代わりに、そのシンボルを呼ぼうとしたタイミングで実行時エラーになる)。 すべての依存を完全に解決するのは面倒なので、あまり使わない機能については無視してもよいだろう。 + 通常の C のビルドなら、configure の次は make するところだが、ここでも emmake を使う。役割はほとんど emconfigure と同様である。指定してある EMCC_CFLAGS という環境変数は、Emscripten の C コンパイラへのフラグで、ここでは ERROR_ON_UNDEFINED_SYMBOLS を無効化している。これにより、コンパイル中に出現した解決できなかったシンボルを無視するようになる (代わりに、そのシンボルを呼ぼうとしたタイミングで実行時エラーになる)。すべての依存を完全に解決するのは面倒なので、あまり使わない機能については無視してもよいだろう。

ここまでを実行すると libs/libphp.a が生成される。これは後で使うので移動させている。

- さて、PHP 処理系をライブラリ化できたので、次に先ほど載せた C のソースコードをビルドしていこう。 Dockerfile と同じ場所に php-wasm.c という名前で保存し、次のようにする。 + さて、PHP 処理系をライブラリ化できたので、次に先ほど載せた C のソースコードをビルドしていこう。Dockerfile と同じ場所に php-wasm.c という名前で保存し、次のようにする。

COPY php-wasm.c /src/
@@ -290,10 +290,10 @@
     :

- emcccc (C コンパイラ/リンカ) の Emscripten 版で、-c は「コンパイル」の意。 -o-I は普通の C コンパイラと同様、出力ファイルの指定とインクルードパスの指定である。 + emcccc (C コンパイラ/リンカ) の Emscripten 版で、-c は「コンパイル」の意。-o-I は普通の C コンパイラと同様、出力ファイルの指定とインクルードパスの指定である。

- libphp.aphp-wasm.o が手に入ったので、これらをリンクして WebAssembly のバイナリとそのラッパである JavaScript ファイルを生成する。 これにも emcc コマンドを使う。 + libphp.aphp-wasm.o が手に入ったので、これらをリンクして WebAssembly のバイナリとそのラッパである JavaScript ファイルを生成する。これにも emcc コマンドを使う。

RUN emcc \
@@ -313,31 +313,31 @@
                 それぞれのフラグについて解説する。
               

- -s ENVIRONMENT=node は、生成する WebAssembly/JavaScript の実行環境を指定する。 今回は node を指定しているので、Node.js 向けのファイルが生成される。 + -s ENVIRONMENT=node は、生成する WebAssembly/JavaScript の実行環境を指定する。今回は node を指定しているので、Node.js 向けのファイルが生成される。

-s ERROR_ON_UNDEFINED_SYMBOLS=0 についてはすでに述べたので省略する。

- -s EXPORTED_RUNTIME_METHODS='["ccall"]' は、生成される JavaScript から公開される API である。 すでに index.mjs で使用しているが、ccall('関数名', '返り値の型', ['仮引数の型', ...], ['実引数', ...]) のように使う。 + -s EXPORTED_RUNTIME_METHODS='["ccall"]' は、生成される JavaScript から公開される API である。すでに index.mjs で使用しているが、ccall('関数名', '返り値の型', ['仮引数の型', ...], ['実引数', ...]) のように使う。

- -s EXPORT_ES6=1 は、JavaScript コードを ECMAScript 6 に準拠した module として生成する。 これを指定することで、require() ではなく import できる JavaScript を生成させられる。 + -s EXPORT_ES6=1 は、JavaScript コードを ECMAScript 6 に準拠した module として生成する。これを指定することで、require() ではなく import できる JavaScript を生成させられる。

-s INITIAL_MEMORY=16777216 は呼んで字のごとく。用途に合わせて適当に決めてほしい。

- -s INVOKE_RUN=0 は、module をロードしたときに勝手に main() を呼ぶかどうか (だと思う)。 今回は php_wasm_run() しか使うつもりがないので切っている。 + -s INVOKE_RUN=0 は、module をロードしたときに勝手に main() を呼ぶかどうか (だと思う)。今回は php_wasm_run() しか使うつもりがないので切っている。

- -s MODULARIZE=1 は、実質的にほぼ必須のオプションであり、1 を指定することで「WebAssembly module をインスタンス化する関数」をエクスポートするような JavaScript ファイルを生成するようになる。 これを指定しないと、生成物の JavaScript ファイルを読み込むと WebAssembly module が即座にインスタンス化されてしまい、起動のタイミングを制御できない。 + -s MODULARIZE=1 は、実質的にほぼ必須のオプションであり、1 を指定することで「WebAssembly module をインスタンス化する関数」をエクスポートするような JavaScript ファイルを生成するようになる。これを指定しないと、生成物の JavaScript ファイルを読み込むと WebAssembly module が即座にインスタンス化されてしまい、起動のタイミングを制御できない。

- ここまで実行すると、php-wasm.jsphp-wasm.wasm が作られる。 では、ここからはこれらの実行環境を作っていこう。 + ここまで実行すると、php-wasm.jsphp-wasm.wasm が作られる。では、ここからはこれらの実行環境を作っていこう。

- といっても、Node.js はビルトインで WebAssembly をサポートしているので、ほとんどやることはない。 先ほど掲載した JavaScript のコードは、Dockerfile と同じディレクトリに index.mjs で配置すること。 + といっても、Node.js はビルトインで WebAssembly をサポートしているので、ほとんどやることはない。先ほど掲載した JavaScript のコードは、Dockerfile と同じディレクトリに index.mjs で配置すること。

FROM node:20.7
@@ -368,7 +368,7 @@
           

まとめ

- ここまでをまとめた Git リポジトリ を用意した。 簡単にコンパイルできるので、興味があれば試してみてほしい。 + ここまでをまとめた Git リポジトリ を用意した。簡単にコンパイルできるので、興味があれば試してみてほしい。

diff --git a/vhosts/blog/public/posts/2023-10-13/i-entered-the-open-university-of-japan/index.html b/vhosts/blog/public/posts/2023-10-13/i-entered-the-open-university-of-japan/index.html index 6cd37645..99656273 100644 --- a/vhosts/blog/public/posts/2023-10-13/i-entered-the-open-university-of-japan/index.html +++ b/vhosts/blog/public/posts/2023-10-13/i-entered-the-open-university-of-japan/index.html @@ -60,7 +60,7 @@

放送大学に入学しました

- とあるきっかけがあり、もう一度大学生をすることにしました。 仕事のほうも、これまでどおりフルタイムで続けていきます。 + とあるきっかけがあり、もう一度大学生をすることにしました。仕事のほうも、これまでどおりフルタイムで続けていきます。

黙っているよりも公表したほうがモチベーションの向上に繋がるだろうと思い、このブログに記事として載せました。 diff --git a/vhosts/blog/public/posts/2023-12-03/isucon-13/index.html b/vhosts/blog/public/posts/2023-12-03/isucon-13/index.html index 721b6c90..077d873c 100644 --- a/vhosts/blog/public/posts/2023-12-03/isucon-13/index.html +++ b/vhosts/blog/public/posts/2023-12-03/isucon-13/index.html @@ -60,7 +60,7 @@

はじめに

- 先日 11月25日、 ISUCON 13 に参加した。 ISUCON への参加は今回が初めてとなる。 私 nsfisis の1人チーム「うつしもゆ」として参加し、最終スコアは 13,580 点だった。使用言語は Go。 + 先日 11月25日、 ISUCON 13 に参加した。ISUCON への参加は今回が初めてとなる。私 nsfisis の1人チーム「うつしもゆ」として参加し、最終スコアは 13,580 点だった。使用言語は Go。

@@ -100,12 +100,12 @@

戦略

- ISUCON で高スコアを出す戦略については、戦闘力の高い方々が良質な記事を書いてくださっている。 ここでは、上述したような低い目標を達成するための戦略について書こうと思う。 + ISUCON で高スコアを出す戦略については、戦闘力の高い方々が良質な記事を書いてくださっている。ここでは、上述したような低い目標を達成するための戦略について書こうと思う。

環境を破壊しない

- ミドルウェアの設定やアプリケーションコードなど、変更を加えるあらゆるものは、必ずバックアップを取るか Git で管理する。 復旧不能になって環境ごと作り直すことだけは必ず避ける。 + ミドルウェアの設定やアプリケーションコードなど、変更を加えるあらゆるものは、必ずバックアップを取るか Git で管理する。復旧不能になって環境ごと作り直すことだけは必ず避ける。

@@ -123,7 +123,7 @@

使い慣れた道具を使う

- 使用する言語、ミドルウェア、ツール類を、使い慣れたものに限定する。 「このツールのオプションはほとんどそらで指定できる」と言えるようなものだけを使う。 「自分では使ったことがないが ISUCON 強者がお勧めしていた」といった理由でツールを選定しない (もちろん、本番までに練習して習熟するという選択肢は存在する)。 + 使用する言語、ミドルウェア、ツール類を、使い慣れたものに限定する。「このツールのオプションはほとんどそらで指定できる」と言えるようなものだけを使う。「自分では使ったことがないが ISUCON 強者がお勧めしていた」といった理由でツールを選定しない (もちろん、本番までに練習して習熟するという選択肢は存在する)。

diff --git a/vhosts/blog/public/posts/2023-12-31/2023-reflections/index.html b/vhosts/blog/public/posts/2023-12-31/2023-reflections/index.html index 86248d17..b3717000 100644 --- a/vhosts/blog/public/posts/2023-12-31/2023-reflections/index.html +++ b/vhosts/blog/public/posts/2023-12-31/2023-reflections/index.html @@ -60,7 +60,7 @@

登壇・カンファレンススタッフ

- 勉強会やカンファレンスで登壇したりスタッフをしたりし始めたのは今年かららしい。 LT 等も含めて計 11 回の登壇をおこなった。 + 勉強会やカンファレンスで登壇したりスタッフをしたりし始めたのは今年かららしい。LT 等も含めて計 11 回の登壇をおこなった。

  • @@ -117,7 +117,7 @@

    書いた記事

    - 登壇が増えたためか記事を書く機会が減ってしまった。 特に社内記事の本数が大きく減少しており、一昨年は約 100 本、昨年は約 60 本の社内記事を書いていたが、今年は 30 本強に留まった。 その頃と比べると文章を書く筋肉が衰えているように感じる。 + 登壇が増えたためか記事を書く機会が減ってしまった。特に社内記事の本数が大きく減少しており、一昨年は約 100 本、昨年は約 60 本の社内記事を書いていたが、今年は 30 本強に留まった。その頃と比べると文章を書く筋肉が衰えているように感じる。

    • diff --git a/vhosts/blog/public/posts/2024-01-10/neovim-insert-namespace-declaration-to-empty-php-file/index.html b/vhosts/blog/public/posts/2024-01-10/neovim-insert-namespace-declaration-to-empty-php-file/index.html index 26724deb..2ed2dd3a 100644 --- a/vhosts/blog/public/posts/2024-01-10/neovim-insert-namespace-declaration-to-empty-php-file/index.html +++ b/vhosts/blog/public/posts/2024-01-10/neovim-insert-namespace-declaration-to-empty-php-file/index.html @@ -90,25 +90,25 @@ LuaJIT 2.1.1693350652

- 今回は Lua で処理を記述したため、Vim では動作しない。以下の説明でも Neovim に絞って述べる。 また、パス区切りがスラッシュである前提で記述したため、Windows には対応していない。 + 今回は Lua で処理を記述したため、Vim では動作しない。以下の説明でも Neovim に絞って述べる。また、パス区切りがスラッシュである前提で記述したため、Windows には対応していない。

ftplugin を用意する

- Neovim には特定のファイルタイプに対して特別な処理をおこなうための ftplugin と呼ばれる仕組みがある。 Neovim の設定を置くディレクトリ (例えば ~/.config/nvim) の配下に ftplugin/<FILE_TYPE>.vim または ftplugin/<FILE_TYPE>.lua というファイルを配置すると、その <FILE_TYPE> が読み込まれたときにそのファイルが自動的に実行される。 + Neovim には特定のファイルタイプに対して特別な処理をおこなうための ftplugin と呼ばれる仕組みがある。Neovim の設定を置くディレクトリ (例えば ~/.config/nvim) の配下に ftplugin/<FILE_TYPE>.vim または ftplugin/<FILE_TYPE>.lua というファイルを配置すると、その <FILE_TYPE> が読み込まれたときにそのファイルが自動的に実行される。

今回は、Neovim がデフォルトで用意している PHP 用 ftplugin が動作したあとに追加の処理をおこないたいので、after/ftplugin/php.{vim,lua} というファイルを配置する。名前から察せられるとおり、after/ftplugin 以下のファイルは ftplugin 以下のファイルよりもあとに実行される。

- この記事では Lua で処理を記述するため、拡張子には .lua を用いる。 これ以降載せるコードは、すべて after/ftplugin/php.lua の中に記述している。 + この記事では Lua で処理を記述するため、拡張子には .lua を用いる。これ以降載せるコードは、すべて after/ftplugin/php.lua の中に記述している。

二重読み込みを防ぐ

- ファイルタイプは読み込んだあとに変更されることもあるので、ftplugin は複数回実行されうる。 二重読み込みを防ぐために、did_ftplugin_<FILE_TYPE>_after というバッファローカル変数を定義しておくのが慣習となっている。 + ファイルタイプは読み込んだあとに変更されることもあるので、ftplugin は複数回実行されうる。二重読み込みを防ぐために、did_ftplugin_<FILE_TYPE>_after というバッファローカル変数を定義しておくのが慣習となっている。

if vim.b.did_ftplugin_php_after then
@@ -267,7 +267,7 @@
           

おわりに

- 簡易的な実装だが、多くのケースではうまく動いているようだ。 最大の問題は PSR 4 に準拠しないフレームワークを用いているとまったく役に立たないことで、今まさに職場で困っている。 こちらはいずれ改良したい。 + 簡易的な実装だが、多くのケースではうまく動いているようだ。最大の問題は PSR 4 に準拠しないフレームワークを用いているとまったく役に立たないことで、今まさに職場で困っている。こちらはいずれ改良したい。

diff --git a/vhosts/blog/public/posts/2024-02-22/phpkansai-2024-report/index.html b/vhosts/blog/public/posts/2024-02-22/phpkansai-2024-report/index.html index 5883fc26..4081d1e9 100644 --- a/vhosts/blog/public/posts/2024-02-22/phpkansai-2024-report/index.html +++ b/vhosts/blog/public/posts/2024-02-22/phpkansai-2024-report/index.html @@ -107,7 +107,7 @@ 本カンファレンスの前日 2024-02-10 は YAPC::Hiroshima に参加しており 、2日連続のカンファレンスとなった。かなり疲れはしたが、その分充実した週末となったように思う。

- 翌3月は PHPerKaigi 2024、4月は PHPカンファレンス小田原 2024 があり、いずれもスタッフ兼スピーカーで参加予定である。 今度は提供する側として、満足のいくカンファレンスになるようにしたい。 + 翌3月は PHPerKaigi 2024、4月は PHPカンファレンス小田原 2024 があり、いずれもスタッフ兼スピーカーで参加予定である。今度は提供する側として、満足のいくカンファレンスになるようにしたい。

diff --git a/vhosts/blog/public/posts/2024-03-17/phperkaigi-2024-report/index.html b/vhosts/blog/public/posts/2024-03-17/phperkaigi-2024-report/index.html index 6870db28..d37dc3ed 100644 --- a/vhosts/blog/public/posts/2024-03-17/phperkaigi-2024-report/index.html +++ b/vhosts/blog/public/posts/2024-03-17/phperkaigi-2024-report/index.html @@ -69,7 +69,7 @@

はじめに

- 2024-03-07 から 2024-03-09 にかけて開催された、 PHPerKaigi 2024 に参加した。 今年はスピーカーとして、また、コアスタッフとして参加した。 + 2024-03-07 から 2024-03-09 にかけて開催された、 PHPerKaigi 2024 に参加した。今年はスピーカーとして、また、コアスタッフとして参加した。

過去の参加レポはこちら: @@ -105,13 +105,13 @@

- WebAssembly の VM を PHP で実装し、実装に至るまでの道程や WebAssembly の特徴、言語処理系を作る楽しさについて語った。 タイトルにある「WebAssembly を理解する」という目的が達成できるようなトークだったかと言われると疑問は残るものの、実際に作った人にしかできない話をすることはできたと思う。 + WebAssembly の VM を PHP で実装し、実装に至るまでの道程や WebAssembly の特徴、言語処理系を作る楽しさについて語った。タイトルにある「WebAssembly を理解する」という目的が達成できるようなトークだったかと言われると疑問は残るものの、実際に作った人にしかできない話をすることはできたと思う。

コアスタッフとして

- 昨年は当日スタッフとして参加したが、今年はコアスタッフとして運営に参加した。 今年はコードゴルフ企画を提案し、その準備とシステムの開発、当日の運用をおこなった。 そのシステムは現在も下記の URL から閲覧でき、当日出題された問題や参加者の方々の回答が見られる。 + 昨年は当日スタッフとして参加したが、今年はコアスタッフとして運営に参加した。今年はコードゴルフ企画を提案し、その準備とシステムの開発、当日の運用をおこなった。そのシステムは現在も下記の URL から閲覧でき、当日出題された問題や参加者の方々の回答が見られる。

Albatross.PHP @@ -128,7 +128,7 @@ RubyVM を PHP で実装する〜Hello World を出力するまで〜 (めもりー さん)

- 今回一番楽しみにしていたセッションであり、期待どおりの面白さだった。 私も今回 VM を作るというテーマで登壇したこともあり、高い解像度で受け取ることができたように思う。 + 今回一番楽しみにしていたセッションであり、期待どおりの面白さだった。私も今回 VM を作るというテーマで登壇したこともあり、高い解像度で受け取ることができたように思う。

P.S. Ask the Speaker で話した、Ruby VM (written in PHP) on PHP VM (compiled to Wasm) on Wasm VM (written in PHP) on PHP というアイデアは「マジ」なので、続報をお待ちください (自作 Wasm runtime に不足している機能を鋭意実装中です)。 @@ -154,7 +154,7 @@ ゴリゴリに開発しなければいけないセッションのスピーカーとゴリゴリに開発しなければいけない企画のスタッフを同じカンファレンスでやってはいけない

- ただ、それでもコアスタッフとして半年ほど関わっただけに、終わってみると感慨深い。 例年どおり、お祭のような活気・熱気を感じることができた。 + ただ、それでもコアスタッフとして半年ほど関わっただけに、終わってみると感慨深い。例年どおり、お祭のような活気・熱気を感じることができた。

来月は、また登壇とスタッフ (こちらは当日スタッフ) をおこなう PHP カンファレンス小田原 があるので、良いトーク・良いカンファレンスを作れるようにしたい。 diff --git a/vhosts/blog/public/posts/2024-04-14/phpcon-odawara-2024-report/index.html b/vhosts/blog/public/posts/2024-04-14/phpcon-odawara-2024-report/index.html index 3eed3621..676416f7 100644 --- a/vhosts/blog/public/posts/2024-04-14/phpcon-odawara-2024-report/index.html +++ b/vhosts/blog/public/posts/2024-04-14/phpcon-odawara-2024-report/index.html @@ -94,7 +94,7 @@ 今回、どこから話を始めるか大いに迷ったのだが、最終的には PHP 処理系の opcode や VM といった概念は既知のものとし、そこから JIT コンパイルへ繋げるといった構成にした。

- PHP の処理系がスクリプトを opcode へ変換する過程については、ちょうど同じカンファレンスの めもりーさんの発表 あたりを参考にしていただくとよいだろう。 また、新しい IR についてより詳しく知りたいという方は、スライド末尾の「参考資料」にあるリンクを参照いただくのがよいかと思う。 + PHP の処理系がスクリプトを opcode へ変換する過程については、ちょうど同じカンファレンスの めもりーさんの発表 あたりを参考にしていただくとよいだろう。また、新しい IR についてより詳しく知りたいという方は、スライド末尾の「参考資料」にあるリンクを参照いただくのがよいかと思う。

Tracing JIT の発火条件や、IR を使って実現される最適化方法など、調べたものの発表に入らなかった話がごまんとあるので、これもどこかに持っていければと考えている。 diff --git a/vhosts/blog/public/posts/2024-04-21/pipefail-option-in-gitlab-ci-cd/index.html b/vhosts/blog/public/posts/2024-04-21/pipefail-option-in-gitlab-ci-cd/index.html index 157dc1ab..f2d709da 100644 --- a/vhosts/blog/public/posts/2024-04-21/pipefail-option-in-gitlab-ci-cd/index.html +++ b/vhosts/blog/public/posts/2024-04-21/pipefail-option-in-gitlab-ci-cd/index.html @@ -128,7 +128,7 @@ set +o pipefail

- こうすると、パイプ全体が失敗するようになる。 この設定は、デフォルトだと off になっている。 + こうすると、パイプ全体が失敗するようになる。この設定は、デフォルトだと off になっている。

@@ -148,10 +148,10 @@ when: always

- grep コマンドは、パターンにマッチする行が一行もなかったとき、exit code 1 を返す。よって、pipefail が on になっていると、このジョブは失敗する。 現在の pipefail がどうなっているか確かめるため set +o で全オプションを出力させたところ、pipefail が on になっていた。 + grep コマンドは、パターンにマッチする行が一行もなかったとき、exit code 1 を返す。よって、pipefail が on になっていると、このジョブは失敗する。現在の pipefail がどうなっているか確かめるため set +o で全オプションを出力させたところ、pipefail が on になっていた。

- しかし、先述したように Bash における pipefail のデフォルト値は off のはずだ。 実際に、ローカルで alpine:latest を動かしてみたところ、 + しかし、先述したように Bash における pipefail のデフォルト値は off のはずだ。実際に、ローカルで alpine:latest を動かしてみたところ、

$ docker run --rm alpine:latest sh -c "set +o"
@@ -179,7 +179,7 @@
           

どこで pipefail が on になるか

- .gitlab-ci.yml で明示的には書いていないので、GitLab Runner (GitLab CI/CD のスクリプトを実行するプログラム) が勝手に追加しているに違いない。 そう仮説を立てて GitLab Runner のリポジトリ を調査したところ、 ソースコード中の以下の箇所set -o pipefail していることが判明した (コメントは筆者による)。 + .gitlab-ci.yml で明示的には書いていないので、GitLab Runner (GitLab CI/CD のスクリプトを実行するプログラム) が勝手に追加しているに違いない。そう仮説を立てて GitLab Runner のリポジトリ を調査したところ、 ソースコード中の以下の箇所set -o pipefail していることが判明した (コメントは筆者による)。

// pipefail オプションが存在しない環境にも対応するため、
diff --git a/vhosts/blog/public/posts/2024-04-29/zsh-file-completion-for-composer-custom-commands/index.html b/vhosts/blog/public/posts/2024-04-29/zsh-file-completion-for-composer-custom-commands/index.html
index 5a7c283d..66682843 100644
--- a/vhosts/blog/public/posts/2024-04-29/zsh-file-completion-for-composer-custom-commands/index.html
+++ b/vhosts/blog/public/posts/2024-04-29/zsh-file-completion-for-composer-custom-commands/index.html
@@ -80,13 +80,13 @@
           

はじめに

- Composer は PHP のデファクトスタンダードなパッケージマネージャである。 Zsh では、composer コマンドに対する補完が提供されており、composer と入力してタブキーを押すと、利用可能なコマンドやオプションが補完される。 Zsh の補完はシェル関数の形で実装されており、composer コマンドに対応した補完をおこなうのは _composer である。 記事執筆時点での補完関数の定義は、GitHub のミラーリポジトリから参照できる。 + Composer は PHP のデファクトスタンダードなパッケージマネージャである。Zsh では、composer コマンドに対する補完が提供されており、composer と入力してタブキーを押すと、利用可能なコマンドやオプションが補完される。Zsh の補完はシェル関数の形で実装されており、composer コマンドに対応した補完をおこなうのは _composer である。記事執筆時点での補完関数の定義は、GitHub のミラーリポジトリから参照できる。

発生していた問題

- composer コマンドはカスタムコマンド (composer.jsonscripts で定義されたコマンド) に対して補完をおこなわない。 つまり、途中まで入力されたカスタムコマンドを補完しないし、カスタムコマンドの引数も補完しない。 例えば、PHPUnit を呼び出す phpunit というカスタムコマンドを定義し composer phpu まで打ってタブキーを押しても、composer phpunit にはならない。 また、composer phpunit -- -- まで打ってタブキーを押しても、phpunit コマンドのオプションは補完されない。 + composer コマンドはカスタムコマンド (composer.jsonscripts で定義されたコマンド) に対して補完をおこなわない。つまり、途中まで入力されたカスタムコマンドを補完しないし、カスタムコマンドの引数も補完しない。例えば、PHPUnit を呼び出す phpunit というカスタムコマンドを定義し composer phpu まで打ってタブキーを押しても、composer phpunit にはならない。また、composer phpunit -- -- まで打ってタブキーを押しても、phpunit コマンドのオプションは補完されない。

このことは、先ほどリンクを載せた _composer 関数を定義しているファイルの冒頭にも書かれている。 @@ -102,22 +102,22 @@

やりたいこと

- 確かに、カスタムコマンドに対して完全な補完を提供するのは不可能か、あるいは実現できても遅くなりすぎるだろう。 しかし、不完全なフォールバックを提供するくらいなら可能なはずだ。 + 確かに、カスタムコマンドに対して完全な補完を提供するのは不可能か、あるいは実現できても遅くなりすぎるだろう。しかし、不完全なフォールバックを提供するくらいなら可能なはずだ。

- この記事では、これらのカスタムコマンドについて、Zsh が提供するデフォルトのファイル・ディレクトリ補完を適用する。 つまり、composer phpunit -- tests/ まで打ってタブキーを押すと、tests ディレクトリの下にあるテストファイルまたはディレクトリが補完される。 + この記事では、これらのカスタムコマンドについて、Zsh が提供するデフォルトのファイル・ディレクトリ補完を適用する。つまり、composer phpunit -- tests/ まで打ってタブキーを押すと、tests ディレクトリの下にあるテストファイルまたはディレクトリが補完される。

解決策

- まずは、Zsh で補完関数を提供する場合のボイラープレートコードを書く。 以下は ~/.zshrc にすべて書く前提だが、autoload を設定するなどすれば別ファイルに分離できる (詳細な手順は割愛)。 + まずは、Zsh で補完関数を提供する場合のボイラープレートコードを書く。以下は ~/.zshrc にすべて書く前提だが、autoload を設定するなどすれば別ファイルに分離できる (詳細な手順は割愛)。

compdef _my_composer composer composer.phar

- compdef は Zsh が用意している関数で、第一引数に補完関数の名前、第二引数以降に補完を適用するコマンド名を並べる。 この場合は、composer コマンドや composer.phar コマンドに対して _my_composer を使って補完をおこなうよう定義している。 + compdef は Zsh が用意している関数で、第一引数に補完関数の名前、第二引数以降に補完を適用するコマンド名を並べる。この場合は、composer コマンドや composer.phar コマンドに対して _my_composer を使って補完をおこなうよう定義している。

次に _my_composer を定義する。基本的にはデフォルトの composer コマンドの補完関数 (つまり _composer 関数) を使い、それが何も返さなかった場合に限り、Zsh のファイル・ディレクトリ補完へフォールバックする。 @@ -128,13 +128,13 @@ }

- _composer コマンドは何も補完候補がなかったとき非ゼロな exit status で終了するので、そうであったなら _files を呼び出す。 _files は、Zsh がデフォルトで用意しているファイル・ディレクトリの補完をおこなう関数である。 + _composer コマンドは何も補完候補がなかったとき非ゼロな exit status で終了するので、そうであったなら _files を呼び出す。_files は、Zsh がデフォルトで用意しているファイル・ディレクトリの補完をおこなう関数である。

まとめ

- これらの設定をおこなうことで、部分的ながら Composer のカスタムコマンドに対して補完をおこなうことができる。 特に、PHPUnit や PHPStan などの対象ファイル・ディレクトリを引数に取るようなコマンドを使う場合に有用であろう。 + これらの設定をおこなうことで、部分的ながら Composer のカスタムコマンドに対して補完をおこなうことができる。特に、PHPUnit や PHPStan などの対象ファイル・ディレクトリを引数に取るようなコマンドを使う場合に有用であろう。

diff --git a/vhosts/blog/public/posts/2024-06-19/scalamatsuri-2024-report/index.html b/vhosts/blog/public/posts/2024-06-19/scalamatsuri-2024-report/index.html index 9f8bafb4..54c1f999 100644 --- a/vhosts/blog/public/posts/2024-06-19/scalamatsuri-2024-report/index.html +++ b/vhosts/blog/public/posts/2024-06-19/scalamatsuri-2024-report/index.html @@ -110,7 +110,7 @@

おわりに

- 私が Scala を書いたり追ったりしていたのは Scala 2 の頃で、Scala 3 はほとんど浦島太郎状態だったのだが、非常に楽しく面白いイベントだった。 イベントに触発されて、長らく塩漬けになっていた Scala 製の趣味プロジェクトを久しぶりに触っているのだが、これもまた楽しい。 + 私が Scala を書いたり追ったりしていたのは Scala 2 の頃で、Scala 3 はほとんど浦島太郎状態だったのだが、非常に楽しく面白いイベントだった。イベントに触発されて、長らく塩漬けになっていた Scala 製の趣味プロジェクトを久しぶりに触っているのだが、これもまた楽しい。

ScalaMatsuri 運営の皆さま、スピーカーの皆さま、スポンサーの皆さま、最高のイベントをありがとうございました!次回も楽しみにしています。 diff --git a/vhosts/blog/public/posts/2024-07-19/reparojson-fix-only-json-formatter/index.html b/vhosts/blog/public/posts/2024-07-19/reparojson-fix-only-json-formatter/index.html index 51d94b92..e6caeb2b 100644 --- a/vhosts/blog/public/posts/2024-07-19/reparojson-fix-only-json-formatter/index.html +++ b/vhosts/blog/public/posts/2024-07-19/reparojson-fix-only-json-formatter/index.html @@ -73,10 +73,10 @@

欲しかったもの

- Vim で JSON を編集しているときに、文法エラー (末尾カンマやカンマの不足) のみを修正して一切の整形をおこなわないプラグインが欲しかった。 整形も同時におこなうプラグインは見つかっただけでも多数あったのだが、整形しないものは見つけられなかったので自作することにした。 + Vim で JSON を編集しているときに、文法エラー (末尾カンマやカンマの不足) のみを修正して一切の整形をおこなわないプラグインが欲しかった。整形も同時におこなうプラグインは見つかっただけでも多数あったのだが、整形しないものは見つけられなかったので自作することにした。

- なお、作成したツール自体は単体の CLI として動作し、Vim とは無関係に使うことができる。 この記事では Neovim と組み合わせる場合の設定を紹介するが、およそ任意のエディタで使えるだろう。 + なお、作成したツール自体は単体の CLI として動作し、Vim とは無関係に使うことができる。この記事では Neovim と組み合わせる場合の設定を紹介するが、およそ任意のエディタで使えるだろう。

@@ -159,7 +159,7 @@ })

- ほとんどは nvim-lspconfig と efm-langserver を使う際のボイラープレートだが、formatCommand-q フラグを指定していることに注意してほしい。 このツールは、デフォルトでは JSON が修正された場合 exit code 1 で終了する。 これは、入力が最初から正しかった場合と修正して正しくなった場合を区別するためだが、異常終了してしまうと置き換えが発生しない。 そのため、-q フラグを指定して、修正されたときも exit code 0 で終了するようにしている。 + ほとんどは nvim-lspconfig と efm-langserver を使う際のボイラープレートだが、formatCommand-q フラグを指定していることに注意してほしい。このツールは、デフォルトでは JSON が修正された場合 exit code 1 で終了する。これは、入力が最初から正しかった場合と修正して正しくなった場合を区別するためだが、異常終了してしまうと置き換えが発生しない。そのため、-q フラグを指定して、修正されたときも exit code 0 で終了するようにしている。

@@ -192,7 +192,7 @@ }

- もちろん、このような操作を文法を壊さずにおこなう Vim プラグインは存在する。 しかし、単なる行の入れ換えであれば ddp の3ストロークでおこなうことができ、専用のキーバインドを覚える必要もない。 このツールを用いることで、より Vimmer-friendly な JSON 編集が可能となる。 + もちろん、このような操作を文法を壊さずにおこなう Vim プラグインは存在する。しかし、単なる行の入れ換えであれば ddp の3ストロークでおこなうことができ、専用のキーバインドを覚える必要もない。このツールを用いることで、より Vimmer-friendly な JSON 編集が可能となる。

diff --git a/vhosts/blog/public/posts/2024-08-19/go-template-access-outer-scope-pipeline-within-with-or-range/index.html b/vhosts/blog/public/posts/2024-08-19/go-template-access-outer-scope-pipeline-within-with-or-range/index.html index 418b5203..18730a09 100644 --- a/vhosts/blog/public/posts/2024-08-19/go-template-access-outer-scope-pipeline-within-with-or-range/index.html +++ b/vhosts/blog/public/posts/2024-08-19/go-template-access-outer-scope-pipeline-within-with-or-range/index.html @@ -66,7 +66,7 @@

はじめに

- Go には、標準ライブラリにテンプレートライブラリ text/template がある。 この text/template における制御構造、withrange は次のように使われる。 + Go には、標準ライブラリにテンプレートライブラリ text/template がある。この text/template における制御構造、withrange は次のように使われる。

# {{ .Title }}
@@ -87,7 +87,7 @@
               text/template. は、現在の操作対象を表す特殊なオブジェクトである。
             

- withrange は、. を変更する効果を持つ。 with は引数に渡されたオブジェクトを . へセットして、内部のテンプレートを実行する。 range は引数に渡されたイテレート可能なオブジェクトに対し、それぞれの要素を . へセットして、要素の個数だけ内部のテンプレートを実行する。 + withrange は、. を変更する効果を持つ。with は引数に渡されたオブジェクトを . へセットして、内部のテンプレートを実行する。range は引数に渡されたイテレート可能なオブジェクトに対し、それぞれの要素を . へセットして、要素の個数だけ内部のテンプレートを実行する。

つまりこのテンプレートは、次のような構造をレンダリングしている (Execute() の第2引数)。 @@ -122,7 +122,7 @@ {{ end }}

- withrange は、. を自身の対象オブジェクトに変更するので、 単に {{ with .User }} の中で .Title と書いても、それは UserTitle プロパティを参照しているとみなされる。 + withrange は、. を自身の対象オブジェクトに変更するので、単に {{ with .User }} の中で .Title と書いても、それは UserTitle プロパティを参照しているとみなされる。

text/template では変数が使えるので、テンプレートの先頭で @@ -152,7 +152,7 @@ {{ end }}

- $ は、テンプレートが実行されるときに渡されたオブジェクトを指す。 これを使えば現在の . に関係なくトップレベルを参照できる。 + $ は、テンプレートが実行されるときに渡されたオブジェクトを指す。これを使えば現在の . に関係なくトップレベルを参照できる。

このことは、text/template の公式ドキュメントにも以下のように記載されている。 diff --git a/vhosts/blog/public/posts/2024-12-04/cohackpp-report/index.html b/vhosts/blog/public/posts/2024-12-04/cohackpp-report/index.html index 2cd7f946..c4f6d334 100644 --- a/vhosts/blog/public/posts/2024-12-04/cohackpp-report/index.html +++ b/vhosts/blog/public/posts/2024-12-04/cohackpp-report/index.html @@ -92,7 +92,7 @@ 私は「ぺ」陣営のスピーカーとして LT をしていたのですが、その前にまずは登壇以外の感想を。

- いや~最高でしたね。どの枠のスピーチの方も良かったのですが、特に (asumikam さん/stefafafan さんに)「育てられた」枠のお二方が印象に残っています。 (asumikam さん/stefafafan さんを)「育てた」枠としてお世話になった方に声をかけることはできると思うんですよ。 それだけでなく、「自分が育てたのだ」と言える人がいて、そしてそれに 100 点で応える人がいるということ。この素晴しさ。人徳。 + いや~最高でしたね。どの枠のスピーチの方も良かったのですが、特に (asumikam さん/stefafafan さんに)「育てられた」枠のお二方が印象に残っています。(asumikam さん/stefafafan さんを)「育てた」枠としてお世話になった方に声をかけることはできると思うんですよ。それだけでなく、「自分が育てたのだ」と言える人がいて、そしてそれに 100 点で応える人がいるということ。この素晴しさ。人徳。

改めて、asumikam さん、stefafafan さん、ご結婚おめでとうございます! @@ -116,7 +116,7 @@

いざ尋常に勝負

- 当日は、「プログラミングマナー講座」と題して発表をおこないました。 結婚式のマナー、特に「忌み言葉」へフォーカスし、これを無理やりプログラミングに適用するというものです。 スライドはこちらにアップロードしています。 + 当日は、「プログラミングマナー講座」と題して発表をおこないました。結婚式のマナー、特に「忌み言葉」へフォーカスし、これを無理やりプログラミングに適用するというものです。スライドはこちらにアップロードしています。

最終的にお祝いのメッセージを仕込んだソースコードで締めるという構成は、我ながら綺麗にまとまったと思っています。忌み言葉の案は他にも大量にあったのですが、技術 LT かつ結婚祝いスピーチにするためにどうしても最後のソースコードが必要だったので、時間の関係上それらには犠牲となってもらいました ( ボツになった案のひとつ )。 diff --git a/vhosts/blog/public/posts/2024-12-33/2024-reflections/index.html b/vhosts/blog/public/posts/2024-12-33/2024-reflections/index.html index 6043a6b9..1ef7b57b 100644 --- a/vhosts/blog/public/posts/2024-12-33/2024-reflections/index.html +++ b/vhosts/blog/public/posts/2024-12-33/2024-reflections/index.html @@ -54,7 +54,7 @@

はじめに

- ご存じのとおり、4 と 11 と 23 で割り切れる年は閏年というやつで 12 月が 33 日まである。 1年の振り返りを書く猶予が平年よりも長くなるので大変に都合がよい。 + ご存じのとおり、4 と 11 と 23 で割り切れる年は閏年というやつで 12 月が 33 日まである。1年の振り返りを書く猶予が平年よりも長くなるので大変に都合がよい。

去年のやつ: /posts/2023-12-31/2023-reflections/ @@ -63,7 +63,7 @@

登壇・カンファレンス参加

- 参加または登壇した勉強会やカンファレンス。 LT 等も含めて計 8 回の登壇をおこなった。 また、4つのカンファレンスでコアスタッフまたは当日スタッフとして参加した。 + 参加または登壇した勉強会やカンファレンス。LT 等も含めて計 8 回の登壇をおこなった。また、4つのカンファレンスでコアスタッフまたは当日スタッフとして参加した。

  • @@ -135,7 +135,7 @@

    書いた記事

    - 今年はこのブログに月1記事以上の記事を書くという目標を立てていた。本数としては 12 本以上あるが、10月と11月はゼロになってしまった。 社内記事を社外向けにリライトする作業を中々進められていないので、2025年は定期的に消化していきたい。 + 今年はこのブログに月1記事以上の記事を書くという目標を立てていた。本数としては 12 本以上あるが、10月と11月はゼロになってしまった。社内記事を社外向けにリライトする作業を中々進められていないので、2025年は定期的に消化していきたい。

    • @@ -154,7 +154,7 @@

      作ったもの

      - 今年は主に WebAssembly ランタイムと、カンファレンスの企画で使うシステムを作っていた。 後者のシステムでもサンドボックス化のための技術として WebAssembly を用いているので、今年は WebAssembly と戯れた一年だったと言える。 + 今年は主に WebAssembly ランタイムと、カンファレンスの企画で使うシステムを作っていた。後者のシステムでもサンドボックス化のための技術として WebAssembly を用いているので、今年は WebAssembly と戯れた一年だったと言える。

      • diff --git a/vhosts/blog/public/posts/2025-01-08/phperkaigi-2023-tokens-q1/index.html b/vhosts/blog/public/posts/2025-01-08/phperkaigi-2023-tokens-q1/index.html index f7021c2f..3e030b6d 100644 --- a/vhosts/blog/public/posts/2025-01-08/phperkaigi-2023-tokens-q1/index.html +++ b/vhosts/blog/public/posts/2025-01-08/phperkaigi-2023-tokens-q1/index.html @@ -82,10 +82,10 @@

        - 2023-03-23 から 2023-03-25 にかけて開催された PHPerKaigi 2023 では、PHPer チャレンジという企画がおこなわれた。 PHPer チャレンジとは、スポンサーのパンフレットやカンファレンス会場などから「#」記号で始まる文字列を集め、景品などを得るという企画である。 この文字列は「PHPer トークン」と呼ばれている。弊社 デジタルサーカス株式会社 からは、トークン問題という形で、PHP に関する問題を解くと PHPer トークンが得られるようになっている問題を出題した。 + 2023-03-23 から 2023-03-25 にかけて開催された PHPerKaigi 2023 では、PHPer チャレンジという企画がおこなわれた。PHPer チャレンジとは、スポンサーのパンフレットやカンファレンス会場などから「#」記号で始まる文字列を集め、景品などを得るという企画である。この文字列は「PHPer トークン」と呼ばれている。弊社 デジタルサーカス株式会社 からは、トークン問題という形で、PHP に関する問題を解くと PHPer トークンが得られるようになっている問題を出題した。

        - PHPerKaigi 2023 の参加レポ でも書いたとおり、この年のトークン問題は「昨年の PHPerKaigi 2022 が終わった段階から作り始め、約半年かけて制作」された。 PHPerKaigi 当日も PHPer チャレンジ解説セッション という形で解説の機会を頂いたのだが、せっかく時間をかけて作題したので記事の形でも残しておこうと思う。 + PHPerKaigi 2023 の参加レポ でも書いたとおり、この年のトークン問題は「昨年の PHPerKaigi 2022 が終わった段階から作り始め、約半年かけて制作」された。PHPerKaigi 当日も PHPer チャレンジ解説セッション という形で解説の機会を頂いたのだが、せっかく時間をかけて作題したので記事の形でも残しておこうと思う。

        この記事では、全5問ある中の第1問について解説する。他の問題については以下のリンクを参照のこと。 @@ -138,7 +138,7 @@

        画像として解釈する

        - まずは素直に画像として見てみよう。 全体は QR コードになっている。適当な QR コードリーダで読み込むと、次のようなテキストが表示されるはずだ。 + まずは素直に画像として見てみよう。全体は QR コードになっている。適当な QR コードリーダで読み込むと、次のようなテキストが表示されるはずだ。

        Guess password. $ echo "password" | php Q1.png >/dev/null
        @@ -147,7 +147,7 @@ メッセージは、この画像の実行方法とこの問題でやるべきこと (パスワードの推測) を示している。

        - 次に QR コードの中央部に目を向けると、小さな文字で「Password is one of the PHPer tokens.」と書かれているのがわかる。 他の PHPer トークンの中から適切な1つを見つけだし、「パスワード」として渡すことで答えとなる PHPer トークンが得られるというわけだ。 + 次に QR コードの中央部に目を向けると、小さな文字で「Password is one of the PHPer tokens.」と書かれているのがわかる。他の PHPer トークンの中から適切な1つを見つけだし、「パスワード」として渡すことで答えとなる PHPer トークンが得られるというわけだ。

        @@ -163,10 +163,10 @@ すでに 「解き方」の節 で示したように、パスワードである PHPer トークンは「#iwillblog」である。これを与えて実行すると正解のトークンが得られる。

        - このパスワードの選択にはとある事情がある。 今回の問題の作問は前回の開催 (PHPerKaigi 2022) 直後からスタートしており、この時点では PHPerKaigi 2023 で登録される PHPer トークンにどのようなものがあるかはまったくわからない状態であった。 作問作業を早期に終わらせるには、次回開催でも確実に使われるであろう定番のトークンを予測して選ぶ必要があったのだ。 かくして、私が知る限り毎回登場しているトークンである「#iwillblog」に白羽の矢が立てられた。 + このパスワードの選択にはとある事情がある。今回の問題の作問は前回の開催 (PHPerKaigi 2022) 直後からスタートしており、この時点では PHPerKaigi 2023 で登録される PHPer トークンにどのようなものがあるかはまったくわからない状態であった。作問作業を早期に終わらせるには、次回開催でも確実に使われるであろう定番のトークンを予測して選ぶ必要があったのだ。かくして、私が知る限り毎回登場しているトークンである「#iwillblog」に白羽の矢が立てられた。

        - なお、解いてくださった方の中には、先頭の「#」を入力せずに何度も試してしまい答えが得られずじまいになった方もいらっしゃるようだった。 問題を置いていたリポジトリにヒントとしてパスワードのトークンが「i」で始まると書いていたのだが、これが意図せずミスリードになってしまった。 これは私のミスである。 + なお、解いてくださった方の中には、先頭の「#」を入力せずに何度も試してしまい答えが得られずじまいになった方もいらっしゃるようだった。問題を置いていたリポジトリにヒントとしてパスワードのトークンが「i」で始まると書いていたのだが、これが意図せずミスリードになってしまった。これは私のミスである。

        @@ -195,7 +195,7 @@ PNG フッタの後ろにあるデータは、画像ビューアには解釈されず、画像の表示には影響を与えない。したがって、PNG フッタの後ろには任意のデータを埋め込むことができる。

        - さて、PHP には、PHP プログラムの始まりを示すための PHP タグ (<?php または <?) がある。 CLI で実行する場合、PHP タグよりも前にあるデータは標準出力へそのまま出力される。 + さて、PHP には、PHP プログラムの始まりを示すための PHP タグ (<?php または <?) がある。CLI で実行する場合、PHP タグよりも前にあるデータは標準出力へそのまま出力される。

        この画像ファイルは次のような構造になっていた。 @@ -240,7 +240,7 @@ // (以下略)

        - IHDRIEND が PNG 画像の一部で、<?php からが実際のプログラムになっている。 もちろんこれを PHP プログラムとして動かすと、PHP タグより前にある PNG 画像としてのデータはそのまま標準出力へと出力されてしまう。 それを防ぐため、QR コードを読み込んだときの実行方法 + IHDRIEND が PNG 画像の一部で、<?php からが実際のプログラムになっている。もちろんこれを PHP プログラムとして動かすと、PHP タグより前にある PNG 画像としてのデータはそのまま標準出力へと出力されてしまう。それを防ぐため、QR コードを読み込んだときの実行方法

        Guess password. $ echo "password" | php Q1.png >/dev/null
        @@ -255,7 +255,7 @@

        実行される PHP プログラム

        - 画像の正体がわかったところで、画像に隠されていた PHP プログラムについて見ていこう。 先ほどは一部しか記載しなかったので、全体を載せる。 なお、ある程度ゴルフしながら書いたので、空白こそ残しているものの可読性は非常に低いことと思う。 + 画像の正体がわかったところで、画像に隠されていた PHP プログラムについて見ていこう。先ほどは一部しか記載しなかったので、全体を載せる。なお、ある程度ゴルフしながら書いたので、空白こそ残しているものの可読性は非常に低いことと思う。

        <?php
        @@ -361,7 +361,7 @@
         fwrite(STDERR, str_replace('403 Forbidden', '401 Unauthorized', $o));

        - これは一体なんなのか。ずばり、難解プログラミング言語の一つ Piet のインタプリタである。 Piet はピエト・モンドリアン (『赤・青・黄のコンポジション』などで知られる抽象画家) の作品にインスピレーションを受けて作られた、画像をソースコードとするプログラミング言語である。 インタプリタは画像の各ピクセルの上を進みながら、色等に応じて特定の処理をおこなっていく。 ここでは詳しい言語仕様については解説しないので、気になる方は Wikipedia の記事「Piet」 などを参照してほしい。 + これは一体なんなのか。ずばり、難解プログラミング言語の一つ Piet のインタプリタである。Piet はピエト・モンドリアン (『赤・青・黄のコンポジション』などで知られる抽象画家) の作品にインスピレーションを受けて作られた、画像をソースコードとするプログラミング言語である。インタプリタは画像の各ピクセルの上を進みながら、色等に応じて特定の処理をおこなっていく。ここでは詳しい言語仕様については解説しないので、気になる方は Wikipedia の記事「Piet」 などを参照してほしい。

        プログラムの冒頭にあるこの箇所 @@ -370,16 +370,16 @@

        $b = unpack('C*', file_get_contents(__FILE__));

        - で __FILE__ つまりこの画像ファイルを読み込んでいる。 先ほど Piet は画像をソースコードにしていると説明した。 そう、今回の問題の画像ファイル Q1.png は、PHP 製 Piet インタプリタであると同時に、Piet のソースコード画像でもあるのだ。 QR コード中央のカラフルな部分が Piet の命令になっている。 + で __FILE__ つまりこの画像ファイルを読み込んでいる。先ほど Piet は画像をソースコードにしていると説明した。そう、今回の問題の画像ファイル Q1.png は、PHP 製 Piet インタプリタであると同時に、Piet のソースコード画像でもあるのだ。QR コード中央のカラフルな部分が Piet の命令になっている。

        Piet のソースコード

        - さて、Piet でどのようなコードが書かれて (いや、描かれて) いるのかを解説したいところだが、今の私にはできそうにない。 というのも、すでに述べたように Piet は「難解プログラミング言語」である。 およそ人が描いたり読んだりするようには作られていない。性質としては、パズルに近い代物である。 + さて、Piet でどのようなコードが書かれて (いや、描かれて) いるのかを解説したいところだが、今の私にはできそうにない。というのも、すでに述べたように Piet は「難解プログラミング言語」である。およそ人が描いたり読んだりするようには作られていない。性質としては、パズルに近い代物である。

        - というわけで、ここではあらましを説明するだけでご容赦いただきたい。 それぞれの部分はおおよそ次のようなことをやっている (再検証・再読解はしていないので大嘘かもしれない)。 + というわけで、ここではあらましを説明するだけでご容赦いただきたい。それぞれの部分はおおよそ次のようなことをやっている (再検証・再読解はしていないので大嘘かもしれない)。

        • @@ -426,10 +426,10 @@ fwrite(STDERR, str_replace('403 Forbidden', '401 Unauthorized', $o));

          - コメントにも書かれているが、この Piet のソースコード画像には誤りがあった。 本来 HTTP のステータスコードを真似るのなら、認証の失敗には 401 を返さなければならない。 しかし、Piet のソースは 403 を返すように書いてしまっていた。 そのことに私が気付いたのは PHPerKaigi 2023 が開催されるひと月前で、その時点で私はこの Piet のソースコードを (ちょうどこの記事でそうなっているのと同じように) 読解できなくなっていた。 さらに悪いことに、正しいメッセージ「401 Unauthorized」は元の「403 Forbidden」よりも3文字長い。 3文字出力が長くなるということは、それだけ Piet で塗るべきピクセルが増えることを意味する。 もはや3文字追加で出力するだけの余白はこの画像に残されていなかった (と思う。腕ききの Piet プログラマならできるかもしれないので挑戦してみてほしい)。 + コメントにも書かれているが、この Piet のソースコード画像には誤りがあった。本来 HTTP のステータスコードを真似るのなら、認証の失敗には 401 を返さなければならない。しかし、Piet のソースは 403 を返すように書いてしまっていた。そのことに私が気付いたのは PHPerKaigi 2023 が開催されるひと月前で、その時点で私はこの Piet のソースコードを (ちょうどこの記事でそうなっているのと同じように) 読解できなくなっていた。さらに悪いことに、正しいメッセージ「401 Unauthorized」は元の「403 Forbidden」よりも3文字長い。3文字出力が長くなるということは、それだけ Piet で塗るべきピクセルが増えることを意味する。もはや3文字追加で出力するだけの余白はこの画像に残されていなかった (と思う。腕ききの Piet プログラマならできるかもしれないので挑戦してみてほしい)。

          - これを解決するために私が選んだのは、インタプリタを改造し、本来のメッセージとは異なるメッセージを無理やり出力させて帳尻を合わせることだった。 そういうわけでこの Piet インタプリタは完全な Piet インタプリタではなく、「403 Forbidden」というテキストを絶対に出力できない。 + これを解決するために私が選んだのは、インタプリタを改造し、本来のメッセージとは異なるメッセージを無理やり出力させて帳尻を合わせることだった。そういうわけでこの Piet インタプリタは完全な Piet インタプリタではなく、「403 Forbidden」というテキストを絶対に出力できない。

        @@ -448,7 +448,7 @@

        おわりに

        - この問題の自己評価はこちら。 問題の出題順はおおよそ作成した順になっているのだが、そのせいで難易度高めの問題が1問目に配置されてしまった。 これは反省点の一つである。 + この問題の自己評価はこちら。問題の出題順はおおよそ作成した順になっているのだが、そのせいで難易度高めの問題が1問目に配置されてしまった。これは反省点の一つである。

        • diff --git a/vhosts/blog/public/posts/2025-01-26/yaml-breaking-changes-between-v1-1-and-v1-2/index.html b/vhosts/blog/public/posts/2025-01-26/yaml-breaking-changes-between-v1-1-and-v1-2/index.html index 2db1aac9..36469811 100644 --- a/vhosts/blog/public/posts/2025-01-26/yaml-breaking-changes-between-v1-1-and-v1-2/index.html +++ b/vhosts/blog/public/posts/2025-01-26/yaml-breaking-changes-between-v1-1-and-v1-2/index.html @@ -73,7 +73,7 @@

          はじめに

          - データ記述言語の一つ YAML には 1.0、1.1、1.2 のバージョンがある。 これらのうち、1.1 と 1.2 の間には無視できない非互換の変更が多く、1.2 に対応していないライブラリもある (Ruby 同梱の yaml など)。 この記事では、YAML 1.1 と YAML 1.2 の主な破壊的変更を紹介する (影響範囲が広いものを抜粋しており、すべての非互換を網羅してはいない)。 + データ記述言語の一つ YAML には 1.0、1.1、1.2 のバージョンがある。これらのうち、1.1 と 1.2 の間には無視できない非互換の変更が多く、1.2 に対応していないライブラリもある (Ruby 同梱の yaml など)。この記事では、YAML 1.1 と YAML 1.2 の主な破壊的変更を紹介する (影響範囲が広いものを抜粋しており、すべての非互換を網羅してはいない)。

          参照した仕様書はこちら: https://yaml.org/spec/1.2.2/ext/changes/ @@ -84,13 +84,13 @@

          Boolean としてパースされるトークンが true / false とその亜種のみに

          - この変更の影響が最も大きいと思われる。 YAML 1.1 では、boolean 値のリテラルとして truefalse のほか yesnoynonoff、それらの大文字バージョンなどが認められていた。 YAML 1.2 では、truefalse、それらの大文字バージョン (TrueTRUEFalseFALSE) のみが boolean としてパースされるようになった。 + この変更の影響が最も大きいと思われる。YAML 1.1 では、boolean 値のリテラルとして truefalse のほか yesnoynonoff、それらの大文字バージョンなどが認められていた。YAML 1.2 では、truefalse、それらの大文字バージョン (TrueTRUEFalseFALSE) のみが boolean としてパースされるようになった。

          八進数リテラルには 0o が必須に

          - C 言語などでは、0 から始まる数字の列を八進数としてパースする。 YAML 1.1 もこれに準じていたが、1.2 からは 0o のプレフィクスが必須となった (“o” は “octal” の “o”)。 プログラミング言語では、Python や Haskell、Swift、Rust などがこの記法を採用している。 + C 言語などでは、0 から始まる数字の列を八進数としてパースする。YAML 1.1 もこれに準じていたが、1.2 からは 0o のプレフィクスが必須となった (“o” は “octal” の “o”)。プログラミング言語では、Python や Haskell、Swift、Rust などがこの記法を採用している。

          @@ -122,7 +122,7 @@

          おわりに

          - 全体的に、There’s more than one way to do it. から There should be one - and preferably only one - obvious way to do it. へ移行しているように思われる。 データ記述言語としては望ましい方向性ではないかと感じる。 + 全体的に、There’s more than one way to do it. から There should be one - and preferably only one - obvious way to do it. へ移行しているように思われる。データ記述言語としては望ましい方向性ではないかと感じる。

          diff --git a/vhosts/blog/public/posts/2025-02-24/phpcon-nagoya-2025-report/index.html b/vhosts/blog/public/posts/2025-02-24/phpcon-nagoya-2025-report/index.html index 8a421f26..a6ff034c 100644 --- a/vhosts/blog/public/posts/2025-02-24/phpcon-nagoya-2025-report/index.html +++ b/vhosts/blog/public/posts/2025-02-24/phpcon-nagoya-2025-report/index.html @@ -108,7 +108,7 @@

          おわりに

          - 今回もカンファレンスくらいでしか聴けないようなセッションがいくつも聴けてよかった。 また、ちょうど連休だったのもあり名古屋も楽しむことができた。 運営のみなさま、お疲れさまでした&ありがとうございました。 次は PHPerKaigi 2025 で会いましょう。 + 今回もカンファレンスくらいでしか聴けないようなセッションがいくつも聴けてよかった。また、ちょうど連休だったのもあり名古屋も楽しむことができた。運営のみなさま、お疲れさまでした&ありがとうございました。次は PHPerKaigi 2025 で会いましょう。

          diff --git a/vhosts/blog/public/posts/2025-03-27/zip-function-like-command-paste-command/index.html b/vhosts/blog/public/posts/2025-03-27/zip-function-like-command-paste-command/index.html index 366e8a72..00c6943e 100644 --- a/vhosts/blog/public/posts/2025-03-27/zip-function-like-command-paste-command/index.html +++ b/vhosts/blog/public/posts/2025-03-27/zip-function-like-command-paste-command/index.html @@ -116,10 +116,10 @@ ' a.txt b.txt > ab.txt

          - paste コマンドは複数のファイルを引数に取り、それらを1行ずつ消費しながら -d で指定した文字で区切って出力する。 -d は区切り文字の指定で、デフォルトだとタブ区切りになる。 + paste コマンドは複数のファイルを引数に取り、それらを1行ずつ消費しながら -d で指定した文字で区切って出力する。-d は区切り文字の指定で、デフォルトだとタブ区切りになる。

          - ファイル名には - を指定でき、その場合は標準入力から読み込んで出力する。 このとき paste - - のように複数回 - を指定すると、指定した回数の行ごとに連結することができる。 例えば ab.txt だとこうなる。 + ファイル名には - を指定でき、その場合は標準入力から読み込んで出力する。このとき paste - - のように複数回 - を指定すると、指定した回数の行ごとに連結することができる。例えば ab.txt だとこうなる。

          $ paste - - < ab.txt
          diff --git a/vhosts/blog/public/posts/2025-03-28/http-1-1-send-multiple-same-headers/index.html b/vhosts/blog/public/posts/2025-03-28/http-1-1-send-multiple-same-headers/index.html
          index 42356f6f..df8307a9 100644
          --- a/vhosts/blog/public/posts/2025-03-28/http-1-1-send-multiple-same-headers/index.html
          +++ b/vhosts/blog/public/posts/2025-03-28/http-1-1-send-multiple-same-headers/index.html
          @@ -95,7 +95,7 @@
                           

          - 【日本語訳 (私が訳したもので、公式なものではない)】 送信者は、同じ field name の header field を複数生成してはならない (MUST NOT)。 ただし、header field の値がコンマ区切りのリストとして定義されているか、header field がよく知られた例外 (後述) である場合はその限りでない。 + 【日本語訳 (私が訳したもので、公式なものではない)】 送信者は、同じ field name の header field を複数生成してはならない (MUST NOT)。ただし、header field の値がコンマ区切りのリストとして定義されているか、header field がよく知られた例外 (後述) である場合はその限りでない。

          @@ -106,7 +106,7 @@

          - 【日本語訳 (私が訳したもので、公式なものではない)】 受信者は、同じ field name を持つ複数の header field を、メッセージの意味を変えないようにしつつ同じ順序で追加して、単一のコンマで区切られた "field-name: field-value" のペアに結合してよい (MAY)。 したがって、同じ field name を持つ header field がどのような順序で受信されたかは、結合された値の解釈に影響する。 よって、プロキシは、メッセージを転送する際、header field の順序を変えてはならない (MUST NOT)。 + 【日本語訳 (私が訳したもので、公式なものではない)】 受信者は、同じ field name を持つ複数の header field を、メッセージの意味を変えないようにしつつ同じ順序で追加して、単一のコンマで区切られた "field-name: field-value" のペアに結合してよい (MAY)。したがって、同じ field name を持つ header field がどのような順序で受信されたかは、結合された値の解釈に影響する。よって、プロキシは、メッセージを転送する際、header field の順序を変えてはならない (MUST NOT)。

          @@ -117,7 +117,7 @@

          - 【日本語訳 (私が訳したもので、公式なものではない)】 注意: 実際には、Set-Cookie header field (RFC6265) は、しばしばレスポンスメッセージ中に複数回現れる。 これはリストの構文を使っておらず、上述した同じ field name を持つ header field についての要件に違反している。 この値は単一の値へ結合できないため、受信者は、header field を処理する際、Set-Cookie を特別扱いした方がよい。 + 【日本語訳 (私が訳したもので、公式なものではない)】 注意: 実際には、Set-Cookie header field (RFC6265) は、しばしばレスポンスメッセージ中に複数回現れる。これはリストの構文を使っておらず、上述した同じ field name を持つ header field についての要件に違反している。この値は単一の値へ結合できないため、受信者は、header field を処理する際、Set-Cookie を特別扱いした方がよい。

          おそらく、「送信側」のところで書かれている「よく知られた例外」の一つがこれだと思われる。 diff --git a/vhosts/blog/public/posts/2025-04-20/trick-2025-most-ruby-on-ruby-award/index.html b/vhosts/blog/public/posts/2025-04-20/trick-2025-most-ruby-on-ruby-award/index.html index a8b4129f..5e1debc5 100644 --- a/vhosts/blog/public/posts/2025-04-20/trick-2025-most-ruby-on-ruby-award/index.html +++ b/vhosts/blog/public/posts/2025-04-20/trick-2025-most-ruby-on-ruby-award/index.html @@ -160,7 +160,7 @@ シンタックスハイライトは、トークナイズとトークン種別に応じた色付けの2段階からなる。

          - トークナイズには Ruby 3.4 からデフォルトのパーサになった Prism を利用している。 Prism.lex() を使うとトークナイズができるので、トークンに付いているソースコード位置の情報を使いつつ元のソースコードを復元する。 + トークナイズには Ruby 3.4 からデフォルトのパーサになった Prism を利用している。Prism.lex() を使うとトークナイズができるので、トークンに付いているソースコード位置の情報を使いつつ元のソースコードを復元する。

          y = 1                 # 現在の行
          @@ -236,7 +236,7 @@
           end

          - トークンの種類 (t.type) またはトークンの文字列表現そのもの (t.value.downcase) を使ってテーブルを引いて振り仮名へ変換している。 このテーブルのキー部分そのものにも振り仮名を振るために、トークンが : で終わっていれば : を取り除いて振り仮名を得ている (例: "value:""value""48746992")。 + トークンの種類 (t.type) またはトークンの文字列表現そのもの (t.value.downcase) を使ってテーブルを引いて振り仮名へ変換している。このテーブルのキー部分そのものにも振り仮名を振るために、トークンが : で終わっていれば : を取り除いて振り仮名を得ている (例: "value:""value""48746992")。

          このテーブルはサイズ制限を突破するために圧縮されており、kana() 関数で展開される。 @@ -263,14 +263,14 @@ # => "バリュー"

          - これは後で気付いたのだが、Ruby は多倍長整数が扱えるので "48746992" のようなデータは単に 48746992 と書けばよかった。 kana() 関数が多少長くはなるが、振り仮名データの数 x 2 バイト分サイズが減るのでこちらの方が短くなる。 サイズ制限の都合で振り仮名を振るのを諦めた記号もあったのでもったいない。 + これは後で気付いたのだが、Ruby は多倍長整数が扱えるので "48746992" のようなデータは単に 48746992 と書けばよかった。kana() 関数が多少長くはなるが、振り仮名データの数 x 2 バイト分サイズが減るのでこちらの方が短くなる。サイズ制限の都合で振り仮名を振るのを諦めた記号もあったのでもったいない。

          おわりに

          - 本っ当に取りたかったので心から嬉しいです。 全部で 3作提出したのですが、他の 2つも選外佳作として選出していただけた上、そのうちの “Least Truthful” については最後に Matz 氏から言及があり、審査員賞と合わせて望外の栄誉となりました。 + 本っ当に取りたかったので心から嬉しいです。全部で 3作提出したのですが、他の 2つも選外佳作として選出していただけた上、そのうちの “Least Truthful” については最後に Matz 氏から言及があり、審査員賞と合わせて望外の栄誉となりました。

          ありがとうございました! diff --git a/vhosts/blog/public/posts/2025-04-24/composer-patches-v2-does-not-require-gnu-patch-even-on-macos/index.html b/vhosts/blog/public/posts/2025-04-24/composer-patches-v2-does-not-require-gnu-patch-even-on-macos/index.html index 9d9dc7a1..487ec2c3 100644 --- a/vhosts/blog/public/posts/2025-04-24/composer-patches-v2-does-not-require-gnu-patch-even-on-macos/index.html +++ b/vhosts/blog/public/posts/2025-04-24/composer-patches-v2-does-not-require-gnu-patch-even-on-macos/index.html @@ -82,7 +82,7 @@ Composer は PHP におけるデファクトスタンダードなパッケージ管理システムである。

          - Composer を拡張するプラグインの一つに、composer-patches という Composer パッケージがある。 これは、Composer でパッケージをインストールするときにそのパッケージへ任意のパッチを当てるプラグインである。 + Composer を拡張するプラグインの一つに、composer-patches という Composer パッケージがある。これは、Composer でパッケージをインストールするときにそのパッケージへ任意のパッチを当てるプラグインである。

          社内で発見しすぐに適用しなければならないバグ修正や、Pull Request こそあるもののなかなかマージされない機能等をすぐさま適用してリリースすることができる。 @@ -94,7 +94,7 @@

          macOS での問題点

          - composer-patches は、macOS で一部のパッチの適用に失敗することが知られている。 関連 issues: + composer-patches は、macOS で一部のパッチの適用に失敗することが知られている。関連 issues:

          • @@ -105,10 +105,10 @@

          - これは、composer-patches の想定する patch コマンドが GNU 実装の patch であることに由来する。 macOS にプリインストールされている patch はいわゆる BSD patch であり、GNU patch とは完全な互換性がない。 + これは、composer-patches の想定する patch コマンドが GNU 実装の patch であることに由来する。macOS にプリインストールされている patch はいわゆる BSD patch であり、GNU patch とは完全な互換性がない。

          - ワークアラウンドとして、macOS にも GNU patch をインストールしてしまうという方法がある。 例: + ワークアラウンドとして、macOS にも GNU patch をインストールしてしまうという方法がある。例:

          $ brew install gpatch
          @@ -124,7 +124,7 @@
                         現在ベータ版である composer-patches v2 では、このワークアラウンドが不要になる (見込み)。
                       

          - 最新の実装では、git apply コマンドが最優先で使われる。 また、Git リポジトリがない場合 (config.preferred-installdist に設定している場合など。デフォルトではそうなる) には git init を使って一時的にリポジトリを作成し、その上で git apply を実行するようになった。 + 最新の実装では、git apply コマンドが最優先で使われる。また、Git リポジトリがない場合 (config.preferred-installdist に設定している場合など。デフォルトではそうなる) には git init を使って一時的にリポジトリを作成し、その上で git apply を実行するようになった。

          この変更により、環境ごとに差異のある patch コマンドへの依存がなくなるので、macOS で composer-patches を使うときの厄介事は解消されるものと思われる。 diff --git a/vhosts/blog/public/posts/2025-05-05/make-tiny-self-hosted-c-compiler/index.html b/vhosts/blog/public/posts/2025-05-05/make-tiny-self-hosted-c-compiler/index.html index 4b7b2a68..299d3f94 100644 --- a/vhosts/blog/public/posts/2025-05-05/make-tiny-self-hosted-c-compiler/index.html +++ b/vhosts/blog/public/posts/2025-05-05/make-tiny-self-hosted-c-compiler/index.html @@ -60,7 +60,7 @@

          はじめに

          - C コンパイラと言えば、世界三大自作したいソフトウェアの一角である。 というわけで 『低レイヤを知りたい人のためのCコンパイラ作成入門』 (以下 compilerbook) 片手に作ることにした。 + C コンパイラと言えば、世界三大自作したいソフトウェアの一角である。というわけで 『低レイヤを知りたい人のためのCコンパイラ作成入門』 (以下 compilerbook) 片手に作ることにした。

          実装する機能を適切に絞ってやればゴールデンウィークの間 (2025-05-03 から 2025-05-06) にセルフホストまで持っていけるのではないか?という仮説を立て、ISO 8601 の表記で 4日間を表す “P4D” を冠して P4Dcc と名付けた。 @@ -92,7 +92,7 @@

          設計

          - ゴールデンウィークの4日間で終わらせたいので、実装する言語機能は最低限に絞ることが必要になる。 今回は次のような設計とした (compilerbook の設計を踏襲しているものは除く)。 + ゴールデンウィークの4日間で終わらせたいので、実装する言語機能は最低限に絞ることが必要になる。今回は次のような設計とした (compilerbook の設計を踏襲しているものは除く)。

          • @@ -333,7 +333,7 @@ compilerbook のようなインクリメンタルな進め方を取らずに、最初から普通の言語処理系のような構成にしたのには理由がある。

            - それは、どのくらいの言語機能があればコンパイラを作るのに十分かをこの時点で見積もるためである。 開発を開始する前にも必要な言語機能にはあたりを付けていたが、実際にプロトタイプを作ってみて、これだけの機能セットがあれば足りるだろうという正確な TODO リストを作りたかった。 実際、このとき作ったチェックリストはこのあともほとんど変わっていない (大きな変化点は、配列型をサポートしないと決めたことくらいか)。 + それは、どのくらいの言語機能があればコンパイラを作るのに十分かをこの時点で見積もるためである。開発を開始する前にも必要な言語機能にはあたりを付けていたが、実際にプロトタイプを作ってみて、これだけの機能セットがあれば足りるだろうという正確な TODO リストを作りたかった。実際、このとき作ったチェックリストはこのあともほとんど変わっていない (大きな変化点は、配列型をサポートしないと決めたことくらいか)。

            このあとは、おおむね compilerbook に従って以下のように機能追加を続けた。 @@ -455,13 +455,13 @@ &*sizeof あたりの実装が終わるとかなり C 言語らしくなっていき楽しい。

            - このあたりから、セルフホストに向けて逆方向からのアプローチも並行しておこなっている。 セルフホストするためには処理系のソースコードで使っている言語機能をすべて実装する必要があるわけだが、これまでは処理系が扱える機能を拡充していくという方向だった。この逆、つまり処理系のソースコードで使っている機能を減らすことでもセルフホストに近付いていく。 + このあたりから、セルフホストに向けて逆方向からのアプローチも並行しておこなっている。セルフホストするためには処理系のソースコードで使っている言語機能をすべて実装する必要があるわけだが、これまでは処理系が扱える機能を拡充していくという方向だった。この逆、つまり処理系のソースコードで使っている機能を減らすことでもセルフホストに近付いていく。

            - 例えば、このコンパイラは typedef をサポートしていないが、開発中ずっと typedef を使わないというのは面倒だ。 そこで、セルフホストがある程度現実的になるまでは構造体を typedef しておいて、途中のどこかで typedef を手で脱糖する。 + 例えば、このコンパイラは typedef をサポートしていないが、開発中ずっと typedef を使わないというのは面倒だ。そこで、セルフホストがある程度現実的になるまでは構造体を typedef しておいて、途中のどこかで typedef を手で脱糖する。

            - これらの作業をおこなうことで、処理系自身のソースコード main.c をパースしてバイナリを出力することができるようになった。 いわゆる第2世代のコンパイラである。この現時点ではまだ第2世代コンパイラは何もできない (何を与えてもクラッシュする)。 + これらの作業をおこなうことで、処理系自身のソースコード main.c をパースしてバイナリを出力することができるようになった。いわゆる第2世代のコンパイラである。この現時点ではまだ第2世代コンパイラは何もできない (何を与えてもクラッシュする)。

          @@ -470,7 +470,7 @@ さて、第2世代コンパイラが手に入ったので、ここからは地獄のデバッグ作業が始まる。多段になっているために問題が起きている箇所の特定が難しい。

          - ……と考えていたのだが、実際のところデバッグは1時間ほどで終わってしまった。 修正したのは1点のみ。 なんのことはない、2日目終了時点でほとんど完成していたわけだ。 + ……と考えていたのだが、実際のところデバッグは1時間ほどで終わってしまった。修正したのは1点のみ。なんのことはない、2日目終了時点でほとんど完成していたわけだ。

          記念すべき (?) 最後のバグはこちら。 @@ -485,13 +485,13 @@ }

          - メモリアドレスから参照先の値を得る際、その型によってロードする命令の種類を変える必要があるのだが、その切替をポインタ型でおこなっていた。 正しくは、そのポインタ型が指す型を元にして切り替えなければならない。 + メモリアドレスから参照先の値を得る際、その型によってロードする命令の種類を変える必要があるのだが、その切替をポインタ型でおこなっていた。正しくは、そのポインタ型が指す型を元にして切り替えなければならない。

          これを修正すると、第2世代コンパイラが第3世代コンパイラを出力できるようになり、その後も第N世代が第N+1世代を生成できるようになった。

          - あとは、第2世代のコンパイラがそれ以降のコンパイラとバイナリレベルで一致するかどうかを確かめればよい。 実際に調べてみると、ほとんどの場所が一致したもののどの世代も 6バイトだけ異なることがわかった。 + あとは、第2世代のコンパイラがそれ以降のコンパイラとバイナリレベルで一致するかどうかを確かめればよい。実際に調べてみると、ほとんどの場所が一致したもののどの世代も 6バイトだけ異なることがわかった。

          一体どこが異なるのか。hexdump の差分がこちら。 @@ -509,7 +509,7 @@ 00015e10 79 70 65 5f 6e 65 77 00 74 79 70 65 5f 6e 65 77 |ype_new.type_new|

          - fatal_errorread_alltokenize type_new はいずれも main.c で定義された関数の名前である。 このことから考えると、これは GCC が埋め込んだシンボルテーブルである可能性が高い。 わずかに異なっている 6バイトは、ランダム生成された何かのように見える。 + fatal_errorread_alltokenize type_new はいずれも main.c で定義された関数の名前である。このことから考えると、これは GCC が埋め込んだシンボルテーブルである可能性が高い。わずかに異なっている 6バイトは、ランダム生成された何かのように見える。

          そこで gcc-s (シンボルテーブルを削除するフラグ) を渡してみると、めでたく2世代目以降のコンパイラのバイナリが完全に一致するようになった。 @@ -525,10 +525,10 @@ 最終的な実装は1900行ほど、所要時間は20時間弱となった。

          - 正直なところ、思ったより早く終わって拍子抜けしている。 これは compilerbook がうまく実装順を整理しているのと、アセンブリの細かい落とし穴を事前に解説して潰していることが大きいと思われる。 + 正直なところ、思ったより早く終わって拍子抜けしている。これは compilerbook がうまく実装順を整理しているのと、アセンブリの細かい落とし穴を事前に解説して潰していることが大きいと思われる。

          - 当初の仮説どおり、サポートする機能を慎重に選ぶことにより短期間でセルフホストまで持っていくことができた。 案外簡単に作れてしまうので、まとまった休みに是非いかがだろうか。 + 当初の仮説どおり、サポートする機能を慎重に選ぶことにより短期間でセルフホストまで持っていくことができた。案外簡単に作れてしまうので、まとまった休みに是非いかがだろうか。

          diff --git a/vhosts/blog/public/posts/2025-06-14/baba-is-you/index.html b/vhosts/blog/public/posts/2025-06-14/baba-is-you/index.html index aaeb52f7..1283ee2b 100644 --- a/vhosts/blog/public/posts/2025-06-14/baba-is-you/index.html +++ b/vhosts/blog/public/posts/2025-06-14/baba-is-you/index.html @@ -63,7 +63,7 @@

          Baba Is You とは

          - Baba Is You という倉庫番系パズルゲームがある。 私がこれまでプレイしたことのあるパズルゲームの中で、間違いなく最高のパズルゲームだと断言できる。 これより面白いパズルゲームを知っている人は、絶対に買うので教えてほしい。 + Baba Is You という倉庫番系パズルゲームがある。私がこれまでプレイしたことのあるパズルゲームの中で、間違いなく最高のパズルゲームだと断言できる。これより面白いパズルゲームを知っている人は、絶対に買うので教えてほしい。

          すでに押しも押されもせぬ傑作としての名をほしいままにする本作だが、名作の感想はいくつあってもいいので書く。 @@ -77,10 +77,10 @@

          どういうゲームか?

          - Baba Is You はいわゆる倉庫番パズルの一種である。 2D のグリッドで操作キャラを動かし、アイテムを押して動かすことでパズルを解く。 + Baba Is You はいわゆる倉庫番パズルの一種である。2D のグリッドで操作キャラを動かし、アイテムを押して動かすことでパズルを解く。

          - Baba Is You の特異な点は、倉庫番のルールが盤面上で動かせるオブジェクトとして配置してある点にある。 これは Baba Is You の一番最初の面である。 + Baba Is You の特異な点は、倉庫番のルールが盤面上で動かせるオブジェクトとして配置してある点にある。これは Baba Is You の一番最初の面である。

          最初の面「BABA IS YOU」のスクリーンショット @@ -127,7 +127,7 @@ 最初の状態では、YOU である baba (うさぎや猫のような白い生き物) が WIN である旗に触れることで勝利条件を満たしクリアとなる。

          - これらのルールを構成しているテキストを押して動かすことで、ルールをさまざまに変化させることができる。 この面なら一例として次のようなルールが作れるだろう。 + これらのルールを構成しているテキストを押して動かすことで、ルールをさまざまに変化させることができる。この面なら一例として次のようなルールが作れるだろう。

          • @@ -161,19 +161,19 @@

          - この「ルール自体を変えられる」という性質により、パズルの難易度・複雑さが大きく上がっている。 プレイヤーは、どのオブジェクトを YOU にするのか、WIN にすべきは何か、どれに PUSH を付けるべきか、いつどの順番でルールを変えるのか、今の手札で作れるルールは何か等と悩みながら、次第に難しくなるパズルと格闘しなければならない。 + この「ルール自体を変えられる」という性質により、パズルの難易度・複雑さが大きく上がっている。プレイヤーは、どのオブジェクトを YOU にするのか、WIN にすべきは何か、どれに PUSH を付けるべきか、いつどの順番でルールを変えるのか、今の手札で作れるルールは何か等と悩みながら、次第に難しくなるパズルと格闘しなければならない。

          ゲームのボリューム・難易度

          - 遊べる面の数は「200 以上」ある (Steam ストアページの表記より引用。正確な個数はここでは控える)。 ただしこれは追加 DLC の分を含んでいないので、実際には更に大量にある。 + 遊べる面の数は「200 以上」ある (Steam ストアページの表記より引用。正確な個数はここでは控える)。ただしこれは追加 DLC の分を含んでいないので、実際には更に大量にある。

          - パズルゲームなのでプレイ時間には大きくブレがあるだろうが、私の場合はノーヒントで 75 時間弱だった。 ゲーム画面を閉じて紙とペンで考えていた時間を含めれば、+10~+20時間といったところだろうか。 + パズルゲームなのでプレイ時間には大きくブレがあるだろうが、私の場合はノーヒントで 75 時間弱だった。ゲーム画面を閉じて紙とペンで考えていた時間を含めれば、+10~+20時間といったところだろうか。

          - パズルの難易度はべらぼうに高い。 ひとつ解くのに数時間かかったり、数日間ひとつも解けなかったりするのはよくある (あくまで全クリを目指す場合)。 + パズルの難易度はべらぼうに高い。ひとつ解くのに数時間かかったり、数日間ひとつも解けなかったりするのはよくある (あくまで全クリを目指す場合)。

          完全クリア以外にもいくつかマイルストーンはあるので、それを目指すのもありだろう。 @@ -187,10 +187,10 @@

          高い難易度

          - すでに書いたが、このゲームは非常に難しい。 ゲームのルールを変えられると聞くと、何でもありの大味なプレイ体験かのように思えるかもしれない。 しかし、適当にルールを弄り回して解けるような面は最序盤くらいにしかなく、ゲームが進んでいくと総当たりすら困難になっていく。 解けない、やれることは全部試したはずだ、ゴールから逆算してもこれ以外ありえないのに実現できない、とにかく解けない。 何度もそう思うことになるだろう。 + すでに書いたが、このゲームは非常に難しい。ゲームのルールを変えられると聞くと、何でもありの大味なプレイ体験かのように思えるかもしれない。しかし、適当にルールを弄り回して解けるような面は最序盤くらいにしかなく、ゲームが進んでいくと総当たりすら困難になっていく。解けない、やれることは全部試したはずだ、ゴールから逆算してもこれ以外ありえないのに実現できない、とにかく解けない。何度もそう思うことになるだろう。

          - それにもかかわらず、理不尽だと感じることは驚くほど少ない。 隠された法則・秘密のルールがないというわけではない。 最初の面を再び例に出そう。 ROCK IS PUSHROCK IS STOP を同時に成立させたら、岩は押せるのか押せないのか。 PUSHSTOP の優先順は言葉で説明されるわけではないし、自分で試して規則を発見することが求められる。 しかし、実際にゲーム上で試しさえすれば、その規則は明らかな結果となってプレイヤーへと提示されるのである。 これを繰り返すことで、プレイヤーは単語ごとの挙動を、そして Baba Is You を理解していく。 + それにもかかわらず、理不尽だと感じることは驚くほど少ない。隠された法則・秘密のルールがないというわけではない。最初の面を再び例に出そう。ROCK IS PUSHROCK IS STOP を同時に成立させたら、岩は押せるのか押せないのか。PUSHSTOP の優先順は言葉で説明されるわけではないし、自分で試して規則を発見することが求められる。しかし、実際にゲーム上で試しさえすれば、その規則は明らかな結果となってプレイヤーへと提示されるのである。これを繰り返すことで、プレイヤーは単語ごとの挙動を、そして Baba Is You を理解していく。

          この特徴により、その難易度に比して理不尽さが大きく低減されていると感じる。 @@ -199,10 +199,10 @@

          新しい単語との出会い

          - このゲームには PUSHSTOPWINYOU 以外にもさまざまな単語がある。 新しい単語が導入されるときは大抵チュートリアル用の簡単な面が用意されており、プレイヤーはそこで新単語を使っていろいろと実験をすることになる。 + このゲームには PUSHSTOPWINYOU 以外にもさまざまな単語がある。新しい単語が導入されるときは大抵チュートリアル用の簡単な面が用意されており、プレイヤーはそこで新単語を使っていろいろと実験をすることになる。

          - それらの単語の中には、一目で「危険」だとわかる奴らがいる。 YOUWIN は最初からいる連中だが、普通のパズルゲームなら操作キャラや勝利条件を変えられるだけでもとんでもないルールブレイカーだろう。 + それらの単語の中には、一目で「危険」だとわかる奴らがいる。YOUWIN は最初からいる連中だが、普通のパズルゲームなら操作キャラや勝利条件を変えられるだけでもとんでもないルールブレイカーだろう。

          危険な匂いのする単語と出会ったときの「こんな単語を許したらとんでもないことになるぞ」という感覚は実際にプレイしなければ味わえない。 @@ -211,10 +211,10 @@

          美しいパズル

          - 私が大好きなとある面の話をしよう。 この面は最終盤に出現する。 これは序中盤で出てきたとある面のリメイクであり、ほんの少しだけ手が加えられている。 オリジナルとリメイク版の差分は1マスの窪みがあるかないか。 この一つの差分だけで、難易度が劇的に上昇している。 片や特筆することのない印象の薄い面、片やゲーム内屈指の高難易度面である。 最終盤にあるがゆえになんとか解けるものの、もし配置順が入れ替わりでもしようものなら (難易度の差を考えれば絶対にありえないことだが)、ほとんどのプレイヤーがここで諦めるだろう。 + 私が大好きなとある面の話をしよう。この面は最終盤に出現する。これは序中盤で出てきたとある面のリメイクであり、ほんの少しだけ手が加えられている。オリジナルとリメイク版の差分は1マスの窪みがあるかないか。この一つの差分だけで、難易度が劇的に上昇している。片や特筆することのない印象の薄い面、片やゲーム内屈指の高難易度面である。最終盤にあるがゆえになんとか解けるものの、もし配置順が入れ替わりでもしようものなら (難易度の差を考えれば絶対にありえないことだが)、ほとんどのプレイヤーがここで諦めるだろう。

          - これを解いたときは、たった1マスの差でこれだけ難しくできるものなのかと感動した。 他にも似たような例はいくつもある。 素晴しい出来の美しいパズルに何度も何度も出会うことができる。 + これを解いたときは、たった1マスの差でこれだけ難しくできるものなのかと感動した。他にも似たような例はいくつもある。素晴しい出来の美しいパズルに何度も何度も出会うことができる。

          @@ -227,7 +227,7 @@ お世辞にも簡単だとは言えないが、苦しむ価値のあるゲームである。

          - この次のセクションからはネタバレありの感想を書くが、プレイしていない人はもちろん、プレイ中で完全クリアしていない人も読まないことを勧める。 その価値があるゲームだと保証する。 + この次のセクションからはネタバレありの感想を書くが、プレイしていない人はもちろん、プレイ中で完全クリアしていない人も読まないことを勧める。その価値があるゲームだと保証する。

          @@ -237,7 +237,7 @@ ではここからは完全クリアしたプレイヤーに向けて話そう。

          - ここでいう「完全クリア」はレベルパックの「Baba Is You」(いわゆる本編) に用意されているパズルをすべて解いた状態を指すことにする。 Steam の場合、全実績解除と読み替えてもよい。 すなわち、「Museum」や「New Adventures」を含まない。 + ここでいう「完全クリア」はレベルパックの「Baba Is You」(いわゆる本編) に用意されているパズルをすべて解いた状態を指すことにする。Steam の場合、全実績解除と読み替えてもよい。すなわち、「Museum」や「New Adventures」を含まない。

          表記

          @@ -267,7 +267,7 @@

        - また、個々のパズルのことはここまでと同様に「面」と呼ぶことにする。 「LEVEL というテキストが指すゲーム上のオブジェクト」は「level」と書く。 + また、個々のパズルのことはここまでと同様に「面」と呼ぶことにする。「LEVEL というテキストが指すゲーム上のオブジェクト」は「level」と書く。

        @@ -287,7 +287,7 @@

        - ここまでスルスル解けていて初めてしばらく止まった面。 また、苦戦して解いた次の面がその面の派生で絶望するという経験をした最初の面。 この瞬間が苦しくもあり楽しくもある。 + ここまでスルスル解けていて初めてしばらく止まった面。また、苦戦して解いた次の面がその面の派生で絶望するという経験をした最初の面。この瞬間が苦しくもあり楽しくもある。

        @@ -300,7 +300,7 @@

        - 高難易度面で当然のように要求されるテクニックの初出。 可能な行動が大きく制限されているのでマシだが、それでも初見時には困惑した。 + 高難易度面で当然のように要求されるテクニックの初出。可能な行動が大きく制限されているのでマシだが、それでも初見時には困惑した。

        @@ -310,7 +310,7 @@

        - お気に入りの面。 MOVE を活用するのも YOU を一時的に消すのも好きなので、両方出てくるこの面は大好き。 + お気に入りの面。MOVE を活用するのも YOU を一時的に消すのも好きなので、両方出てくるこの面は大好き。

        @@ -330,7 +330,7 @@

        - これも初見時に苦戦した面。 PRISON などと同様に一度理解すれば何ということのない面だが、最初に解けたときは偶然だった。 FLAG IS WIN がギリギリ取り出せそうに「見える」のが嫌らしい。 + これも初見時に苦戦した面。PRISON などと同様に一度理解すれば何ということのない面だが、最初に解けたときは偶然だった。FLAG IS WIN がギリギリ取り出せそうに「見える」のが嫌らしい。

        @@ -340,7 +340,7 @@

        - SHIFT 重ねの初出面。 このテクニックを再び使うのは終盤になってからであり、私はそのときにはもう SHIFT 重ねを忘れていたので大苦戦した。 戯れにスロット2を使って2周目をやっていてこの面まで到達し、そこでようやく SHIFT 重ねが MOVE もどきになることを思い出した。 その意味でも印象深い面。 + SHIFT 重ねの初出面。このテクニックを再び使うのは終盤になってからであり、私はそのときにはもう SHIFT 重ねを忘れていたので大苦戦した。戯れにスロット2を使って2周目をやっていてこの面まで到達し、そこでようやく SHIFT 重ねが MOVE もどきになることを思い出した。その意味でも印象深い面。

        @@ -350,7 +350,7 @@

        - MAP の前半 (~DEEP FOREST) では最も苦戦した面。 SWAP の理解が固まっておらず、「こういう状況が作れたら解けそうだ」という勘が働かなかった。 正直なところ SWAP は今も苦手意識がある (終盤で強制的に学ばされる SHIFT と違って、それほど高難度面での出番がないのも大きいと思う)。 + MAP の前半 (~DEEP FOREST) では最も苦戦した面。SWAP の理解が固まっておらず、「こういう状況が作れたら解けそうだ」という勘が働かなかった。正直なところ SWAP は今も苦手意識がある (終盤で強制的に学ばされる SHIFT と違って、それほど高難度面での出番がないのも大きいと思う)。

        @@ -360,7 +360,7 @@

        - MAP の中で一番苦しんだ面。 実は一度ここで投げて諦めたのだが、EMPTY を理解した今となっては脳内でも瞬殺できるくらい簡単になってしまった。 最初に面を見てから解き終わるまでの時間は間違いなく最長で、半年以上かかっている (他は長くとも数日間)。 + MAP の中で一番苦しんだ面。実は一度ここで投げて諦めたのだが、EMPTY を理解した今となっては脳内でも瞬殺できるくらい簡単になってしまった。最初に面を見てから解き終わるまでの時間は間違いなく最長で、半年以上かかっている (他は長くとも数日間)。

        @@ -370,7 +370,7 @@

        - 難しい面ではあるのだが、それ以上に解法の美しさに感動した面。 解き終わった後に思わず「美しい……」と呟いてしまったのはこの面だけだった。 Baba Is You の好きな面はと聞かれれば真っ先にこれを挙げる。 + 難しい面ではあるのだが、それ以上に解法の美しさに感動した面。解き終わった後に思わず「美しい……」と呟いてしまったのはこの面だけだった。Baba Is You の好きな面はと聞かれれば真っ先にこれを挙げる。

        @@ -390,7 +390,7 @@

        - MAP の問題児。正攻法がテキスト重ねである最初の面。 この面、ICE/LAVA IS PUSH を作ったあと重なった ice と lava を (ICE IS PUSH だけ作るなどして) 分離しないといけないのだが、意気揚々と ice on lava の状態で door に向かって push して push できなかったときの感情はよく覚えている。 テキスト同士を重ねて A IS PUSHB IS PUSH を両立させるというぶっ飛んだアイデアを実現してもなお解けないのか、この方針がまさか間違っているなどということがあるのか、いやそんなはずはない……。 実際のところそこからのリカバリーはすぐできたが、そのときの絶望はこれまででも最大であった。 + MAP の問題児。正攻法がテキスト重ねである最初の面。この面、ICE/LAVA IS PUSH を作ったあと重なった ice と lava を (ICE IS PUSH だけ作るなどして) 分離しないといけないのだが、意気揚々と ice on lava の状態で door に向かって push して push できなかったときの感情はよく覚えている。テキスト同士を重ねて A IS PUSHB IS PUSH を両立させるというぶっ飛んだアイデアを実現してもなお解けないのか、この方針がまさか間違っているなどということがあるのか、いやそんなはずはない……。実際のところそこからのリカバリーはすぐできたが、そのときの絶望はこれまででも最大であった。

        @@ -400,7 +400,7 @@

        - ここも好きな面。 FURTHER FIELDS の精神的後継のようなものなので当然かもしれない。 せっせと働く bird がかわいい。 + ここも好きな面。FURTHER FIELDS の精神的後継のようなものなので当然かもしれない。せっせと働く bird がかわいい。

        @@ -410,16 +410,16 @@

        - MAP の印象的な面と言えば、これを取り上げないわけにはいかない。 LEVEL IS A による level の変換がおこなえる初の面である。 ここまで Baba Is You を進めたプレイヤーであれば、初出のテキストが現れたらまずはその場の色々なテキストと組み合わせてみて相互作用を確認する。 それを見事に利用されたというか、気付かずにはいられないように仕向けられているというか、本当によくできたゲームである。 + MAP の印象的な面と言えば、これを取り上げないわけにはいかない。LEVEL IS A による level の変換がおこなえる初の面である。ここまで Baba Is You を進めたプレイヤーであれば、初出のテキストが現れたらまずはその場の色々なテキストと組み合わせてみて相互作用を確認する。それを見事に利用されたというか、気付かずにはいられないように仕向けられているというか、本当によくできたゲームである。

        MAP

        - MAP 自身。FRAGILE EXISTENCE でそれに気付いたなら当然 HOSTILE ENVIRONMENT でも気付くし、MAP で baba が操作できることに気付いたならもちろん右下のルールに目を向ける。 次に考えるのは無論こうだ。ここで flag を取ったらどうなるんだ? このゲームはその疑問に答えてくれる。期待をはるかに上回る形で。 + MAP 自身。FRAGILE EXISTENCE でそれに気付いたなら当然 HOSTILE ENVIRONMENT でも気付くし、MAP で baba が操作できることに気付いたならもちろん右下のルールに目を向ける。次に考えるのは無論こうだ。ここで flag を取ったらどうなるんだ? このゲームはその疑問に答えてくれる。期待をはるかに上回る形で。

        - ??? でまたしても待ち構える BABA IS YOU と不穏な LEVEL のテキスト、そして GLITCH の W E L C O M E。 間違いなく最高のパズルゲームだと確信した。 + ??? でまたしても待ち構える BABA IS YOU と不穏な LEVEL のテキスト、そして GLITCH の W E L C O M E。間違いなく最高のパズルゲームだと確信した。

        @@ -432,7 +432,7 @@

        - ??? で大いに苦戦した面のひとつ。 PRISON と DUNGEON で既出のテクニックが肝だが、ちと離れすぎじゃないのか。 この面のリメイクもあるが、ここで苦しんだからかそちらはあまり苦戦しなかった。 + ??? で大いに苦戦した面のひとつ。PRISON と DUNGEON で既出のテクニックが肝だが、ちと離れすぎじゃないのか。この面のリメイクもあるが、ここで苦しんだからかそちらはあまり苦戦しなかった。

      @@ -442,7 +442,7 @@

      - 普通に解くだけなら大したことのない面だが、問題はこれの LEVEL IS TEXT 解である。 出現順に書いているのでここに置いたが、解いたのはもっと後、META の後半に差しかかった頃になる。 ??? コンプリートの実績が取れていないことに気付き、残っているとすればここの LEVEL IS TEXT しかないと考えたまではよかったが、そこからが大変だった。 個人的にこのゲームで一番苦しかったのがここの TEXT 変換解である。 単純な難しさに加え、実績が取れていない原因がこの面だという確信も持てなかったので、解けるかどうかわからない状態で挑み続けることとなり疲弊した。 + 普通に解くだけなら大したことのない面だが、問題はこれの LEVEL IS TEXT 解である。出現順に書いているのでここに置いたが、解いたのはもっと後、META の後半に差しかかった頃になる。??? コンプリートの実績が取れていないことに気付き、残っているとすればここの LEVEL IS TEXT しかないと考えたまではよかったが、そこからが大変だった。個人的にこのゲームで一番苦しかったのがここの TEXT 変換解である。単純な難しさに加え、実績が取れていない原因がこの面だという確信も持てなかったので、解けるかどうかわからない状態で挑み続けることとなり疲弊した。

      @@ -450,7 +450,7 @@

      - ??? の DO IT YOURSELF 以降を開くにはこの面の LEVEL IS TEXT が必須だと思っていたのだが、どうもそうではないらしい。 いずれにせよそれを思いつけなかったので同じことか。 + ??? の DO IT YOURSELF 以降を開くにはこの面の LEVEL IS TEXT が必須だと思っていたのだが、どうもそうではないらしい。いずれにせよそれを思いつけなかったので同じことか。

      @@ -465,7 +465,7 @@

      - 両方とも難しい面ではあったが、単文字のテキストが綺麗に活用された美しい面として印象に残っている。 G R A S S IS H O T をこれほど無駄なく使えるとは! + 両方とも難しい面ではあったが、単文字のテキストが綺麗に活用された美しい面として印象に残っている。G R A S S IS H O T をこれほど無駄なく使えるとは!

      @@ -475,7 +475,7 @@

      - 初見のインパクト大にして難易度も相応に高い良作。 この頃はまだ SHIFT理解していなかったので大変だったが、ここを越えたことでむしろこの後の難所が楽になったと言える。 + 初見のインパクト大にして難易度も相応に高い良作。この頃はまだ SHIFT理解していなかったので大変だったが、ここを越えたことでむしろこの後の難所が楽になったと言える。

    @@ -491,7 +491,7 @@

    - DEPTHS の序盤で道を塞いでいる必須面であるにもかかわらず圧倒的難易度で立ちはだかる凶悪な面。 大苦戦した挙句 LEVEL IS BELT を作ってアレ?となったのは私だけではないはず。 + DEPTHS の序盤で道を塞いでいる必須面であるにもかかわらず圧倒的難易度で立ちはだかる凶悪な面。大苦戦した挙句 LEVEL IS BELT を作ってアレ?となったのは私だけではないはず。

@@ -501,7 +501,7 @@

- 取れる行動が多いこと、もう少しで解けそうなルートが多いこと、そのどれもが一筋縄ではいかないこと。 それらがすべて揃った高難度面。 昔のバージョンでは ??? に置いてあったらしい。そんなバカな。 + 取れる行動が多いこと、もう少しで解けそうなルートが多いこと、そのどれもが一筋縄ではいかないこと。それらがすべて揃った高難度面。昔のバージョンでは ??? に置いてあったらしい。そんなバカな。

@@ -517,7 +517,7 @@

- 難しいとか難しくないとかじゃなくここは触れざるをえない。 FLAG/TEXT 解以外はそれほど苦戦しなかったが、とにもかくにもクリアの要求回数が多すぎる。 もちろん最短で進められるなら別だが、META での試行錯誤のためにはこいつの形を毎回変えなければならない。 しかもどの変換もそれなりにステップ数を要するのが厄介である。 印象に残った面であるのは確か。 + 難しいとか難しくないとかじゃなくここは触れざるをえない。FLAG/TEXT 解以外はそれほど苦戦しなかったが、とにもかくにもクリアの要求回数が多すぎる。もちろん最短で進められるなら別だが、META での試行錯誤のためにはこいつの形を毎回変えなければならない。しかもどの変換もそれなりにステップ数を要するのが厄介である。印象に残った面であるのは確か。

@@ -527,7 +527,7 @@

- よくぞこの面を作ってくれた。 外の level を参照させるギミックは、LEVEL IS A の変換をやりだした頃からいつかあるはずと思っていたので、そのとおりのパズルが出てきてくれて嬉しい。 これぞ Baba Is You。 + よくぞこの面を作ってくれた。外の level を参照させるギミックは、LEVEL IS A の変換をやりだした頃からいつかあるはずと思っていたので、そのとおりのパズルが出てきてくれて嬉しい。これぞ Baba Is You。

@@ -537,7 +537,7 @@

- 前半のネタバレなし感想にも書いたが、これは SCENIC POND のリメイクであり、ほとんど差異がない。 たった1マス窪みが無くなっただけである。 それだけでここまで難しくできるのか。 + 前半のネタバレなし感想にも書いたが、これは SCENIC POND のリメイクであり、ほとんど差異がない。たった1マス窪みが無くなっただけである。それだけでここまで難しくできるのか。

最終盤に配置されていたことで難易度の割には苦戦しなかったが、難易度以上にリメイクの美しさに感動した面。 @@ -548,7 +548,7 @@

初見時難易度ランキング

- 最後に、初見時の難易度を 10 位までランキングにしてみた。 あくまで初見のときの難易度なので、面自体の難易度ではない。 解くのにかかった時間とも少し違う。 あえて言うなら苦しんだ順。 + 最後に、初見時の難易度を 10 位までランキングにしてみた。あくまで初見のときの難易度なので、面自体の難易度ではない。解くのにかかった時間とも少し違う。あえて言うなら苦しんだ順。

  1. -- cgit v1.2.3-70-g09d2