徹底解説！ マクロの等価性を理解する

:::note info
これは「[TeX & LaTeX Advent Caleandar 2022](https://adventar.org/calendars/7868)」の25日目の記事です。
（24日目は [golden_lucky さん](https://zenn.dev/k16/articles/df1821bdc8097d)です。）
:::

**​「やっぱりTeX言語（とか）しましょう」​**を重点テーマとする「[TeX & LaTeX Advent Calendar 2022][texadvent]」もいよいよクリスマス:christmas_tree:の日を迎えることになりました。最終日となる今日は**​「TeX言語におけるマクロの等価性」​**という少しニッチな話題を扱うことにします。

## 前提知識

というわけで、これはガチTeX言語者:sushi:向けの記事です。幸せなLaTeXユーザ:blush:の皆さんは[例によって][as-usual]アドベントカレンダーの[フィナーレ][finale]を平和に待つことにましょう:christmas_tree::snowman:

本記事の「キホンの話」までの内容については以下の知識を仮定します。

* TeX言語の「トークン(token)」「マクロ(macro)」についてのキホン的な知識。
* 「`\ifx`が何をするものか」についての概略。

そこより先の内容については、さらに以下の知識を仮定します。

* 「文字トークンのカテゴリコード」についてのキホン的な知識。

## 復習:book:の話

TeX言語における「トークンの等価性」を判定する条件文（if-トークン）には`\if`・`\ifcat`・`\ifx`の3つがありますが、その中でも使う機会が一番多いのは`\ifx`でしょう。この`\ifx`は、2つのトークンの**意味**が等しいかを判定するif-トークンです。

* `\ifx«トークン1»«トークン2»`：〔if-トークン〕 `«トークン1»`と`«トークン2»`の**意味**が同じであるか否か。

ここで、比較対象の2つのトークンの現在の意味がともにマクロ（つまり“マクロ型の値”）である場合は、「2つの**マクロの等価性**」が問題になります。この記事では、TeX言語においてマクロの等価性がどう決められているのかについて解説します。

## 準備:pencil2:の話

2つのトークンの等価性を判定して結果を表示するマクロを用意しておきましょう。

```tex
%% \Check<トークン1><トークン2>
% 2つのトークンの意味が等しいなら"SAME"、そうでないなら"DIFFERENT"
% を端末に表示する。
\def\Check#1#2{%
  \immediate\write16{\ifx#1#2SAME\else DIFFERENT\fi}}
```

この`\Check`マクロは以下のように使います。

```tex
\Check AA %==>SAME
\Check AB %==>DIFFERENT
```

以降に示す例においては、実際に`\Check`を実行したときの結果を上記の形式で書くことにします。

## キホン:slight_smile:の話

まずは、フツーのTeX言語の参考書に載っているレベルの説明から始めましょう[^ifx-1]。

[^ifx-1]: 「マクロの等価性」は専ら`\ifx`での判定で使われる概念なので、TeX言語の参考書においては`\ifx`の解説のところで扱われることが多いはずです。

2つのマクロは「次の3要素の全てについて等しい」場合に等価となります。

* **置換テキスト**（replacement text）
* **パラメタテキスト**（parameter text）
* **接頭辞**（prefix）の設定状態

これらの用語に聞きなじみのない人もいると思われるので、ちょっと補足しておきましょう。

```tex
\long\def\macroA#1;#2{#2-#1}
```

「置換テキスト」というのは“マクロの定義の本体”のトークン列のことで、上掲の`\macroA`の定義では`#2-#1`となります。また「パラメタテキスト」は“引数の書式指定”のトークン列のことで、この例では`#1;#2`となります[^paramtok-1]。「接頭辞の設定状態」はこの例では「`\long`付」となります。ここで考慮対象となるマクロの接頭辞には元祖TeXの範囲では`\long`と`\outer`があり、e-TeX拡張においてはこれに`\protected`が追加されます[^prefix-1]（`\global`については後述）。

[^paramtok-1]: 「パラメタトークン」の扱いについては後で詳しく述べます。それまでは取りあえず「`#1`はそのまま2トークンとして扱われる」と思ってかまいません。
[^prefix-1]: これらの接頭辞の機能が何かを説明することは本記事のスコープ外とします。

### 等価になる場合

最も自明なのは2つのマクロに対する定義文[^defstat-1]が全く同じ場合です。当然“3要素”が全て同じになるので、2つのマクロは等価になります。

[^defstat-1]: 本記事では「マクロを定義する作用をもつ（`\def`・`\edef`・`\gdef`・`\xdef`のプリミティブをもつ）文」のことを**定義文**と呼ぶことにします。

```tex
% パラメタテキスト＝“#1;#2”
% 置換テキスト＝“#2-#1”
% 接頭辞設定状態＝“\long 付”
\long\def\macroA#1;#2{#2-#1}
\long\def\macroB#1;#2{#2-#1}
\Check\macroA\macroB %==>SAME
```

また、定義文のソースコード上での文字列が異なっていても、結局それらが同じトークン列に解釈されるのであれば、やはり等価になります。

```tex
% パラメタテキスト＝“#1\and #2.”
% 置換テキスト＝“#1␣\&␣#2\par”
% 接頭辞設定状態＝“なし”
\def\macroA#1\and#2.{#1 \& #2\par}
% 行末は空白文字トークン、空行は \par トークンになるので
% 結局 \macroA と同じ定義文になる。
\def\macroB  #1\and  #2.{%
  #1
  \&
  #2%

}
\Check\macroA\macroB %==>SAME
```

接頭辞設定状態は「マクロ定義時に各接頭辞が付いていたか否か」のみで区別されるので、定義文における接頭辞の出現の順序は考慮されません。

```tex
% パラメタテキスト＝“#2-#1”
% 置換テキスト＝“#1;#2”
% 接頭辞設定状態＝“\protected・\long 付”
\long\protected\def\macroA#1;#2{#2-#1}
\protected\long\def\macroB#1;#2{#2-#1}
\Check\macroA\macroB %==>SAME
```

### 等価にならない場合

“3要素”の何れかの点で2つのマクロが異なる場合は等価になりません。

パラメタテキストが異なる例です。

```tex
%↓パラメタテキスト＝“#1;#2”
\def\macroA#1;#2{#2-#1}
%↓パラメタテキスト＝“#1,#2”
\def\macroB#1,#2{#2-#1}
\Check\macroA\macroB %==>DIFFERENT
```

置換テキストが異なる例です。

```tex
%↓置換テキスト＝“#2A#1”
\def\macroA#1;#2{#2A#1}
%↓置換テキスト＝“#2B#1”
\def\macroB#1;#2{#2B#1}
\Check\macroA\macroB %==>DIFFERENT
```

接頭辞設定状態が異なる例です。

```tex
%↓接頭辞設定状態＝“\protected・\long 付”
\protected\long\def\macroA#1;#2{#2-#1}
%↓接頭辞設定状態＝“\long 付”
          \long\def\macroB#1;#2{#2-#1}
\Check\macroA\macroB %==>DIFFERENT
```

### やっぱり等価にならない場合

パラメタテキストや置換テキストに対して「トークン列の一致」を判断する基準は **“外形”** です。以下の例では`\RELAX`は`\relax`と同じ意味をもちますが異なる制御綴である（つまり“外形”が異なる）ので、`\macroA`と`\macroB`の置換テキストは同一とは見なされません。

```tex
\let\RELAX\relax
%↓置換テキスト＝“#2\relax #1”
\def\macroA#1;#2{#2\relax#1}
%↓置換テキスト＝“#2\RELAX #1”
\def\macroB#1;#2{#2\RELAX#1}
%“\relax”と“\RELAX”は外形が異なるので、2つのマクロの置換テキストは異なる。
\Check\macroA\macroB %==>DIFFERENT
```

また「トークン列の一致」を判断する際には**展開は行われません**。以下の例では、`\RELAX\RELAX`は完全展開すると確かに`\relax\relax`になるはずですが、それでも`\macroA`と`\macroB`の置換テキストは異なると見なされます。

```tex
\def\RELAX{\relax}
%↓置換テキスト＝“\relax \relax”
\def\macroA#1;#2{\relax\relax}
%↓置換テキスト＝“\RELAX \RELAX”
\def\macroB#1;#2{\RELAX\RELAX}
% 展開せずに比較するので、2つのマクロの置換テキストは異なる。
\Check\macroA\macroB %==>DIFFERENT
```

### なにげに等価になる場合

先ほどの説明ではマクロの接頭辞には`\protected`・`\long`・`\outer`の3つがあると述べました。実はマクロの定義**文**についてはもう1つ、`\global`という接頭辞も存在します。しかしこの`\global`は定義の**実行の様態**（定義を大域的に行う）を表すものであるため、定義されるマクロ**自体の状態**としては反映されません。従って、定義文における`\global`の有無[^gdef-1]はマクロの等価性に影響しません。

[^gdef-1]: `\def`と`\gdef`の違い（および`\edef`と`\xdef`の違い）についても同様です。

```tex
%↓接頭辞定義状態＝“なし”
       \def\macroA#1;#2{#2-#1}
%↓\global は反映されないので接頭辞定義状態は“なし”となる。
\global\def\macroB#1;#2{#2-#1}
\Check\macroA\macroB %==>SAME
% \gdef は \global\def と等価なのでこれも同じ結果になる。
\gdef\macroB#1;#2{#2-#1}
\Check\macroA\macroB %==>SAME
```

定義文ではなくマクロ自体の中身のみが考慮されるため、マクロを`\def`で定義したか`\edef`で定義したかも（それ自体は）考慮されません。結果的に同じ置換テキストに帰着するのであれば、それらは等価と見なされます。

```tex
\def\RELAX{\relax}
%↓置換テキスト＝“\relax \relax”
\def\macroA#1;#2{\relax\relax}
%↓置換テキストは完全展開されるので“\relax \relax”となる。
\edef\macroB#1;#2{\RELAX\RELAX}
% 結果的に両者の置換テキストは一致する。
\Check\macroA\macroB %==>SAME
```

次の例は先の例と似ていますが、`\let`で等価定義した場合は「`\relax`に展開される」わけではないので両者は異なる結果になります。

```tex
\let\RELAX\relax
%↓置換テキスト＝“\relax \relax”
\def\macroA#1;#2{\relax\relax}
%↓\RELAX は展開不能なので置換テキストはそのまま“\RELAX \RELAX”となる。
\edef\macroB#1;#2{\RELAX\RELAX}
\Check\macroA\macroB %==>DIFFERENT
% もちろん後で \RELAX を展開可能に変えても \macroB 自体は何も変わらない。
\def\RELAX{\relax}
\Check\macroA\macroB %==>DIFFERENT
```

## チョットアレ:sushi:な話

### カテゴリコードが絡む場合

トークン列の“外形”の一致を判断する際には、文字トークンについては文字コードとカテゴリコードの両方が考慮されます。そのため、文字コードが同じでもカテゴリコードが異なる場合は等価になりません。

:::note info  
本記事では「カテゴリコードが11の`@`の文字トークン」のことを`@₁₁`のように表記します。これはTeX言語の参考書で一般的に用いられる表記法です。
:::


```tex
%↓置換テキスト＝“#2@₁₁#1”
\catcode`\@=11 \def\macroA#1;#2{#2@#1}
%↓置換テキスト＝“#2@₁₂#1”、“@”のカテゴリコードが異なる。
\catcode`\@=12 \def\macroB#1;#2{#2@#1}
\Check\macroA\macroB %==>DIFFERENT
```

:::note warn  
上掲の例で`\macroA`と`\macroB`に`\meaning`を適用して得られる文字列はともに  
`macro:#1;#2->#2@#1`  
となり一致します（“\\the-文字列化”する際にカテゴリコードの違いは消えてしまうため）。この例から「2つのトークンについて、その`\meaning`の文字列が一致していてもそれらが等価であるとは限らない」ということがわかります。
:::

もちろん、カテゴリコードが同じで文字コードが異なる場合も同様です。

```tex
\catcode`\<=1 \catcode`\>=2
%↓置換テキスト＝“#2-₁₂\foo {₁#1}₂”
\def\macroA#1;#2{#2-\foo{#1}}
%↓置換テキスト＝“#2-₁₂\foo <₁#1>₂”、文字コードが異なる。
\def\macroA#1;#2{#2-\foo<#1>}
\Check\macroA\macroB %==>DIFFERENT
```

このように文字トークンの“外形”には文字コードとカテゴリコードの両方の情報が含まれますが、一方で、制御綴の“外形”をみる際に「その制御綴の表記中のエスケープ文字が何であったか」は考慮されません[^csname-1]。従って、以下の例において`\?`と`!?`は“外形において等価”と見なされ、結果的に2つのマクロが等価になります。

[^csname-1]: そもそも制御綴のトークンは`\csname`でも生成できるわけで、この場合には「制御綴の表記中のエスケープ文字」という概念は意味を成しません。

:::note info  
TeX言語の参考書では制御綴のトークンを $\fbox{relax}$ のような枠付の文字列で表現することもあります。こちらの表現のほうが「制御綴の外形はエスケープ文字を含まない」ことが理解しやすいかもしれません。
:::

```tex
\catcode`\!=0 %“!”をエスケープ文字にする
%↓置換テキスト＝“#2\?\foo {#1}”
\def\macroA#1;#2\relax{#2\?\foo{#1}}
%↓“\?”と“!?”はそもそも区別されないので置換テキストは \macroA と同じになる。
\def\macroB#1;#2!relax{#2!?!foo{#1}}
\Check\macroA\macroB %==>SAME
```

なお、定義文におけるカテゴリコードや文字コードの違いがマクロ自体に影響しない場合もあります。例えば、以下の例では、定義文において`{₁}₂`を使ったか`<₁>₂`を使ったかは置換テキストには（その他の要素にも）反映されないので、2つのマクロは等価になります。

```tex
\catcode`\<=1 \catcode`\>=2
%↓全体を囲む { } 自体は置換テキストには含まれないので置換テキストは“#2-₁₂#1”。
\def\macroA#1;#2{#2-#1}
%↓こちらも < > 自体は置換テキストには含まれないので置換テキストは“#2-₁₂#1”。
\def\macroB#1;#2<#2-#1>
\Check\macroA\macroB %==>SAME
```

もちろん、「波括弧区切り(brace delimited)」にした場合[^bracedel-1]はその波括弧がパラメタテキストと置換テキストに含まれることになります。

[^bracedel-1]: マクロ定義文のパラメタテキストの末尾、つまり`{₁`（等のグループ開始文字トークン）の直前に`#₆`（等のパラメタ文字トークン）を置くと、そのマクロは「波括弧区切り」になります。この定義は「パラメタテキストと置換テキストの両方の末尾に当該のグループ開始文字トークンを追加する」という処理によって実現されます。

```tex
\catcode`\<=1 \catcode`\>=2
%↓パラメタテキスト＝“#1;₁₂#2{₁”；置換テキスト＝“#2-₁₂#1{₁”
\def\macroA#1;#2#{#2-#1}
%↓パラメタテキスト＝“#1;₁₂#2<₁”；置換テキスト＝“#2-₁₂#1<₁”
\def\macroB#1;#2#<#2-#1>
\Check\macroA\macroB %==>DIFFERENT
```

### パラメタトークンが絡む場合

先ほど、文字トークンの外形の一致では文字コードとカテゴリコードの両方が考慮されると述べました。ところがこれには一見例外に思えるものがあります。次のように、置換テキストにおいて文字コードが異なる**パラメタ文字トークン**を含めた場合、その文字コードの違いは考慮されないのです:astonished:

:::note warn  
「**パラメタ文字トークン**(parameter character token)」は「カテゴリコードが6である文字トークン」を指します。後で紹介する「**パラメタトークン**(parameter token)」と混同しないように注意してください。
:::

```tex
\catcode`\&=6 % パラメタ文字にする
%↓置換テキストは“#₆2₁₂-₁₂#₆1₁₂”のはず…。
\def\macroA#1;#2{#2-#1}
%↓置換テキストは“#₆2₁₂-₁₂&₆1₁₂”のはず…。
\def\macroB#1;#2{#2-&1}
% なのになぜか等価であると判定される！
\Check\macroA\macroB %==>SAME
```

なぜパラメタ文字トークンだけが例外になるのかというと、その理由は「マクロのパラメタテキストや置換テキストの中においては、`#1`のような**マクロパラメタは特殊扱いになっている**」からです。具体的には、`#1`のようなマクロパラメタの部分はそのまま`#₆1₁₂`として保存されるのではなく、**パラメタトークン**と呼ばれる特殊[^paramtok-2]なトークンに置き換えられた形で保存されます。

[^paramtok-2]: パラメタトークンはマクロ（“マクロ型の値”）を構成するパラメタテキストや置換テキストのトークンの中にのみ存在するという意味で、極めて特殊な存在です。入力バッファのトークンの中には決して含まれないため、例えば「パラメタトークンと等価な制御綴」のようなものを作り出すことは原理的にできません。

ただしパラメタトークンは一種類だけ存在するのではありません。`#1`と`#2`は異なるパラメタを表すのでこの違いは保持されないといけません。このため、置換テキスト中のパラメタトークンは「`#1`に相当するもの」から「`#9`に相当するもの」までの9種類が存在しています。これを`1ₐ`～`9ₐ`で表すことにしましょう[^subargu-1]。

[^subargu-1]: 添字の`a`は“argument”（引数）の略です。

そうすると、置換テキストのトークン列の保存の際には  
`#₆1₁₂`→`1ₐ`、`#₆2₁₂`→`2ₐ`、……、`#₆9₁₂`→`9ₐ`  
という置き換えが発生することになります。ここで、パラメタトークンには「元のパラメタ文字トークン（`#₆`など）の文字コードが何であったか」の情報は残っていないことに注意しましょう。すると、例えば`&₆`のような「別の文字コードのパラメタ文字トークン」を用いたとしても  
`&₆1₁₂`→`1ₐ`、`&₆2₁₂`→`2ₐ`、……、`&₆9₁₂`→`9ₐ`  
と置き換えられることになります。

これを踏まえた上で、もう一度先の例を見てみましょう。

```tex
\catcode`\&=6 % パラメタ文字にする
\def\macroA#1;#2{#2-#1}
\def\macroB#1;#2{#2-&1}
```

パラメタトークンでの置き換えを考慮すると以下のようになり、両者の置換テキストは一致します。

* `\macroA`の置換テキストは`#₆2₁₂-₁₂#₆1₁₂`→`2ₐ-₁₂1ₐ`
* `\macroB`の置換テキストは`#₆2₁₂-₁₂&₆1₁₂`→`2ₐ-₁₂1ₐ`

結局、「置換テキスト中の**パラメタ文字トークンの文字コードの違い**」はマクロの等価性には**影響しない**ことがわかりました。

ところで、TeX言語のマクロ定義の文法では、パラメタ文字トークンは**暗黙の文字トークン[^implicit-1]も許容される**ことになっています[（参考記事）][paramchar]。パラメタ文字トークンを暗黙にすることはマクロの等価性に影響するのでしょうか。実際に調べてみましょう。

[^implicit-1]: 制御綴やアクティブ文字に対して非アクティブ文字を`\let`したもの。

```tex
\let\HASH=# % 暗黙のパラメタ文字トークン
%↓置換テキスト＝“2ₐ-₁₂1ₐ”
\def\macroA#1;#2{#2-#1}
%↓やっぱり置換テキストは“2ₐ-₁₂1ₐ”となる。
\def\macroB#1;#2{#2-\HASH1}
\Check\macroA\macroB %==>SAME
```

あくまで「1番目のパラメタ」を表すパラメタトークンは`1ₐ`しか存在しないので、「元のパラメタ文字トークンが暗黙か明示か」の情報も残らないのでした。

## 超絶アレ:sushi::sushi::sushi:な話

前節では「置換テキストに文字コードの異なるパラメタ文字トークンを使う」話をしました。それでは、**パラメタテキスト**に文字コードの異なるパラメタ文字トークンを使った場合はどうなるでしょう。フツーに考えると置換テキストの場合と同じ結果になりそうですが念のため確認してみましょう……。

```tex
\catcode`\&=6 % パラメタ文字にする
%↓パラメタテキストは“1ₐ;₁₂2ₐ”のはず…。
\def\macroA#1;#2{#2-#1}
%↓パラメタテキストは“1ₐ;₁₂2ₐ”のはず…。
\def\macroB#1;&2{#2-#1}
% なのになぜか等価でないと判定される！
\Check\macroA\macroB %==>DIFFERENT
```

アレレ、置換テキストの場合と異なり、マクロが等価で**ない**と判定されてしまいました。なんということでしょう:astonished::astonished::astonished:

なぜこんなことになるかというと、同じ“パラメタトークン”といっても**パラメタテキスト用と置換テキスト用ではその扱いが異なっている**から、というのがその理由です。恐らく、内部の実装の都合でそうしているのがマクロの等価性という性質を通して“微妙に外に漏れてしまっている”という**アレ:sushi:な事案**なのでしょう:frowning:

パラメタテキスト用のパラメタトークンが置換テキスト用のそれと異なる点は**​「文字コードを保持している」​**ことです。つまり（符号空間が8ビットである元祖TeXでいうと）「文字コードが0のもの」から「文字コードが255のもの」までの256種類が存在するわけです。そこで、例えば`X`の文字コードをもつ（パラメタテキスト用の）パラメタトークンを`Xₚ`で表すことにしましょう[^subparam-1]。パラメタテキストのトークン列中でのマクロパラメタは以下のように置き換えられます。  
`X₆1₁₂`→`Xₚ`、`X₆2₁₂`→`Xₚ`、……、`X₆9₁₂`→`Xₚ` （`X`は任意の文字）  
おや、パラメタの番号（1～9）の情報が消えてしまっています。でもこれで問題はありません。なぜかというと「パラメタテキストにおいては[^paramnum-1]マクロパラメタは番号順に並んでいなければならない」という規則が存在するからです[^paramnum-2]。

[^subparam-1]: 添字の`p`は“parameter”（パラメタ）の略です。
[^paramnum-1]: 先の例で`\macroA`や`\macroB`の置換テキストが`#2-#1`であることからわかるように、「マクロパラメタは番号順に並ぶ」という性質は置換テキストにおいては成立しません。
[^paramnum-2]: 例えばパラメタトークンで置き換えた後のパラメタテキストが`_₈Aₚ_₈Bₚ_₈Cₚ_₈`だったとすると、`Aₚ`・`Bₚ`・`Cₚ`がそれぞれ1番目・2番目・3番目のマクロパラメタであることは明らかです。つまり元のトークン列は`_A₆1_B₆2_C₆3_`だったわけです。

先の例をもう一度見てみましょう。

```tex
\catcode`\&=6 % パラメタ文字にする
\def\macroA#1;#2{#2-#1}
\def\macroB#1;&2{#2-#1}
```

先述の規則を適用すると、パラメタトークンでの置き換えの結果は以下のようになります。

* `\macroA`のパラメタテキストは`#₆1₁₂;₁₂#₆2₁₂`→`#ₚ;₁₂#ₚ`
* `\macroB`のパラメタテキストは`#₆1₁₂;₁₂&₆2₁₂`→`#ₚ;₁₂&ₚ`

この通り、`#ₚ`と`&ₚ`は異なるパラメタトークンであるため、2つのマクロのパラメタテキストは一致しないのでした。

前節で「暗黙のパラメタ文字トークンを置換テキストに使う」話をしましたが、容易に予想されるように、暗黙のパラメタ文字トークンはパラメタテキストでも使用できます。

```tex
\let\HASH=# % 暗黙のパラメタ文字トークン
\def\macroA#1;#2{#2-#1}
\def\macroB#1;\HASH2{#2-#1}
```

この場合に`\macroA`と`\macroB`は等価になるでしょうか。これまでの解説を読んでいれば答えがわかるはずなので、一度自分でじっくりと考えてみてください:snowman:

![scthinkingtime.png](https://qiita-image-store.s3.amazonaws.com/0/14550/c2dd4130-d1bd-0f93-2d03-eb68e7ad6e5a.png)

正解は……。

```tex
\let\HASH=# % 暗黙のパラメタ文字トークン
%↓パラメタテキスト＝“#ₚ;₁₂#ₚ”
\def\macroA#1;#2{#2-#1}
%↓パラメタテキスト＝“#ₚ;₁₂#ₚ”
\def\macroB#1;\HASH2{#2-#1}
% 両者のパラメタテキストが一致するので等価になる。
\Check\macroA\macroB %==>SAME
```

この通り**等価になります**。パラメタテキスト用のパラメタトークンは文字コードの情報はもっていますが「元のパラメタ文字トークンが暗黙であったか」の情報をもつ余地はありません。明示と暗黙の`#₆`はともに`#ₚ`に置き換えられて区別がつかないのでした。

**おつかれさまでした**:smiley:

## まとめ

:blush:**​「メリークリスマス！」​**:gift::snowflake::christmas_tree::snowman:

[texadvent]: https://adventar.org/calendars/7868
[as-usual]: https://qiita.com/zr_tex8r/items/8fad42e4b8bad284e984#%E5%89%8D%E6%8F%90%E7%9F%A5%E8%AD%98
[paramchar]: https://hak7a3.hatenablog.com/entry/2016/08/28/184803
[finale]: https://zrbabbler.hatenablog.com/entry/2022/12/25/233607