[LaTeX] 証明環境が連動する定理環境を作る

## はじめに

LaTeX で何か「定理」とその「証明」を書く際に、「証明」の方にもカウンタを付けようと思ったら皆さんはどうしますか？

たとえば```theorem```パッケージを使うのであれば、以下のようにすることもできるでしょう。

```latex
\newtheorem{theorem}{定理}
\newtheorem{proof}{証明}
...
\begin{theorem}[Felmat の最終定理]
    冪が2より大きいとき、その冪乗数を2つの冪乗数の和に分けることはできない．
    label{theorem:Felmat}
\end{theorem}
\begin{proof}[定理\ref{theorem:Felmat}の証明]
    この定理に関して，私は真に驚くべき証明を見つけたが，この余白はそれを書くには狭すぎる．
    label{proof:Felmat}
\end{proof}
```

この場合、```thorem```環境と```proof```環境のカウンタは完全に独立です。
しかし「証明」は必ず「定理」ありきなので、これらのカウンタが必ず一致するようになると便利ですよね。

さて一方で、ちょっとした定理とその証明をささっと書く場合には、数字がつかない方が好都合ということもあります。
そういったケースでも同じコマンドで処理ができたら楽だと思いませんか。

今日はこれを実現するマクロを作っていきます！

## 完成イメージ

```latex
\begin{theorem}[Felmat の最終定理](Felmat)
    冪が2より大きいとき、その冪乗数を2つの冪乗数の和に分けることはできない．
\end{theorem}
\begin{proof}(Felmat)
    この定理に関して，私は真に驚くべき証明を見つけたが，この余白はそれを書くには狭すぎる．
\end{proof}
```

とすれば

![xparseenv01.jpg](https://qiita-image-store.s3.amazonaws.com/0/105378/1c81dae9-7ede-c704-05dc-67288d61e079.jpeg)


と表示され、```\theoremref{Felmat}```で定理が／```\proofref{Felmat}```で証明が参照できるようにします。

一方で、

```latex
\begin{theorem}
    2つの平方数の和で表される平方数は無数に存在する．
\end{theorem}
\begin{proof}
    読者への演習課題とする．
\end{proof}
```

とすれば以下のように数字の付かないようにします。
![xparseenv02.jpg](https://qiita-image-store.s3.amazonaws.com/0/105378/3bd99318-d9e5-a5d3-44dd-4181a4677f36.jpeg)


なお```(Felmat)```の部分だけでなく、```[Felmat の最終定理]```の部分もオプション引数なので、
見出しの有無を調整できます。

## 作り方

### 材料（用いるパッケージ）

* amsthm（自分でカウンタ等を駆使して環境を作るのも可）
* xparse
* refcount

### 手順

1. amsthm で番号付けのある定理・証明環境とない定理・証明環境を作る
2. xparse で番号付けのある環境とない環境を出し分ける
3. refcount で```proof```環境のカウンタを```theorem```環境のカウンタに一致させる

では、順を追って説明しましょう。

## 詳細

### 1. amsthm で番号付けのある定理・証明環境とない定理・証明環境を作る

```latex
% 番号付けあり
\begin{_theorem}[〈見出し〉]
    〈本文〉
\end{_theorem}
% 番号付けなし
\begin{_theorem*}[〈見出し〉]
    〈本文〉
\end{_theorem*}
```

という環境を作っていきます（番号付けありの場合のカウンタ名は```_theorem```）。証明環境の方も同様です。

ここでは、スタイルの調整が比較的簡単かつ番号なし環境もすぐ作れるamsthmパッケージを利用しますが、
枠線で囲みたいなどスタイルを細かく調整したいのであれば、自分で理想的な環境を作って頂いて構いません。

amsthmで新しい定理環境を作るには、

```latex
\newtheoremstyle{〈スタイル名〉}{〈上部スペース〉}{〈下部スペース〉}{〈本文フォント〉}{〈インデント量〉}{〈見出しフォント〉}%
{〈見出し後の句読点〉}{〈見出し後のスペース〉}{〈見出し書式〉}% 1. 新しいスタイルを作る
\theoremstyle{〈スタイル名〉}% 2. 上記スタイルを有効にする
\newtheorem{〈環境名〉}{〈環境見出し〉}% 3. 有効になっているスタイルの環境を作る。
```
という風に定理スタイルを作ったうえで、それに従う定理環境を作ります。
```newtheoremstyle```が引数多くてビビりますが、大丈夫です、よく分からんところは空欄にしておけば良い感じの既定値が使われます。

作例はこんな感じ。

```latex
% 定理環境（内部用）：_theorem, _theorem*
\newtheoremstyle{mytheorem}{}{}{}{}{\bfseries}%
{\\}{0.5zw}{\underline{\thmname{#1}\thmnumber{#2}\thmnote{\hspace{0.5em}#3}：}}% 1. 新しいスタイルを作る
\theoremstyle{mytheorem}% 2. 上記スタイルを有効にする
\newtheorem{_theorem}{定理}% 3. 有効になっているスタイルの環境を作る。
\newtheorem*{_theorem*}{定理}% 3. 〃　（\newtheorem*は番号なし環境を作るコマンド）
```

```latex
% 証明環境（内部用）：_proof, _proof*
\newtheoremstyle{myproof}{}{}{}{}{\bfseries}%
{\\}{0.5zw}{\underline{\thmnumber{定理#2の}\thmname{#1}\thmnote{\hspace{0.5em}（#3）}：}}% 1. 新しいスタイルを作る
\theoremstyle{myproof}% 2. 上記スタイルを有効にする
\newtheorem{_proof}{証明}% 3. 有効になっているスタイルの環境を作る．
\newtheorem*{_proof*}{証明}% 3. 〃　（←番号なし環境を作るコマンド）
```

一番ややこしいのは```newtheoremstyle```の最後のオプションである見出し書式のところですね。

なお、```#1```で環境見出し（「定理」など）、```#2```で番号、```#3```が見出し文（「Felmatの最終定理」など）が取得できます。
```\thmnote{}```は```#3```が空の時は空文字を返すので、この内側に見出し文を装飾するコマンドを入れることができます（今回の例では、定理環境の方は```\hspace{0.5em}```を入れ、証明環境の方は括弧をつけました）。
同様に```\thmname{}```は```#1```が空なら、```\thmnumber{}```は```#2```が空なら空文字を返します。

あと注意するのは、日本語で書く際は見出しが斜体になるのが嫌なので、見出しフォントに```\bfseries```をしている点くらいでしょうか。


### 2. xparse で番号付けのある環境とない環境を出し分ける

さて前節で、定理と証明のそれぞれについて番号付けのある環境とない環境を作りました。
これらを以下のように同じコマンドで統一的に扱えるようにしましょう。

```latex
\begin{theorem}[〈見出し〉](〈ラベル〉)
    〈本文〉
\end{theorem}
```

見出しもラベルもオプション引数です。
ラベル（2つ目のオプション引数）が指定されているかどうかに応じて、番号付けのある環境とない環境のどちらを出すか切り替えます。

これを実現するには、xparseというパッケージが提供する```NewDocumentEnvoironment```コマンドを使います。

```latex
\NewDocumentEnvironment{〈環境名〉}{〈引数仕様〉}%
{〈環境開始コマンドの定義〉}%
{〈環境終了コマンドの定義〉}
```

引数仕様は、引数指定子を引数の数だけ並べたものです。引数指定子の一覧は下記の通り。

| | デフォルトのもの | 独自の記号を使うもの |
|:-:|:-:|:-:|
| 必須引数 | m | r() |
| オプション引数（既定値なし）| o | d() |
| オプション引数（既定値あり）| O{〈既定値〉} | D(){〈既定値〉}
| 付加記号の有無 | s | t? |

ここで独自の記号を使うというのは、引数の囲み記号に独自のカッコを使えるということです。
上記の例では```()```を使っていますが、山カッコなどの他の記号も使えるはずです。
また最終行の「付加記号の有無」ですが、コマンド名に```*```が付いているかどうかを引数にとるということです
（```\section{}```と```\section*{}```の違いを見分けられるようになる、ということ）。
これも同様に独自の記号が使えます。上記の例では```?```を指定しています。
さらに、各引数指定子の直前に```+```をつけるとその引数は改行を含む文を受け取れるようになります。

今回は、```[]```で囲うオプション引数と```()```で囲うオプション引数を使います。
前者はオプションが指定されなければそのまま空文字を内側の環境に投げればいいので、既定値を空文字にします。
一方後者は既定値のないオプション引数にします。
というわけで、引数仕様は以下のようになります。

```latex
O{} d()
```

こうすると、2つ目のオプションが指定されなかった場合、
```#2```にはNoValueマーカと呼ばれる特殊な値がセットされます。
xparseにはこのNoValueマーカに応じて処理を振り分ける

```latex
IfNoValueTF{〈引数〉}{〈値がない場合のコマンド〉}{〈値がある場合のコマンド〉}
```

というコマンドが用意されているので、これを利用して、以下の様に環境を定義しました。

```latex
% 定理環境：theorem
\NewDocumentEnvironment{theorem}{O{} d()}%
{\IfNoValueTF{#2}{\begin{_theorem*}[#1]}{\begin{_theorem}[#1]}}%開始コマンド
{\IfNoValueTF{#2}{\end{_theorem*}}{\label{theorem:#2}\end{_theorem}}}%終了コマンド
```

簡単ですね！　証明環境の方も、やはり同様に作ります。
ただし、amsthmでは既に```proof```環境が存在しているので、```NewDocumentEnvoironment```ではなく```RenewDocumentEnvoironment```で上書きします。

```latex
% 証明環境：proof
\RenewDocumentEnvironment{proof}{O{} d()}%
{\IfNoValueTF{#2}{\begin{_proof*}[#1]}{\begin{_proof}[#1]}}%開始コマンド
{\IfNoValueTF{#2}{\end{_proof*}}{\label{proof:#2}\end{_proof}}}%終了コマンド
```

あ、終了コマンドの方で環境を閉じる前にラベルをセットしている点に注意してください。
こうすることによって、```\ref{}```で参照できるようになります。

```latex
% 参照用コマンド
\newcommand{\thoremref}[1]{定理\ref{theorem:#1}}
\newcommand{\proofref}[1]{定理\ref{proof:#1}の証明}
```
などと定義しておくとより便利です（利用するときに意識するのは、上記で作った環境の2つ目の引数で設定した```Felmat```といったラベルですが、内部的には```theorem:Felmat```、```proof:Felmat```のように接頭辞を付けて扱っています）。


### 3. refcount で```proof```環境のカウンタを```theorem```環境のカウンタに一致させる

こうして1つのコマンドで番号のありなしを同時に取り扱うことができるようになりました。
いよいよここからが本題（？）です。

この時点では```theorem```環境（正確には```_theorem```環境）と
```proof```環境（やはり正確には```_proof```環境）のカウンタは独立しています。
そのため、記載する順番が逆転したり証明を省略したりした場合には、両環境の番号はとうぜん一致しません。

そこで、```proof```環境の内部で逐一```\setcounter```を呼び出してカウンタをセットし直すことにしましょう。

このアイデアを素朴に実装すると、こうなります。

```latex
\setcounter{_proof}{\ref{theorem:〈ラベル〉}}
```

しかし、これはどうもうまく行きません。
詳しい原因は私には分からないのですが、```\ref{}```で取得できるのが数字そのもののみならず諸々装飾された状態のものになっているのではないかと推測できます。
そこで```\ref{}```コマンドの代わりに活躍してくれるのが
refcountパッケージにある```\getrefnumber{}```です（使い方は全く一緒）。

```latex
\setcounter{_proof}{\getrefnumber{theorem:〈ラベル〉}}
```

さてこれでめでたしめでたし！かと言うとそうは行きません。
なぜなら、TeXのカウンタは（```page```カウンタを除き）「インクリメントしてから表示する」仕様になっているからです。
そのため、このままでは定理の番号に+1したものが表示されてしまいます。
これを回避するには、```\setcounter```でセットする値から予め1引いておくしかありません。
これには、整数の算術式を取り扱うための```\numexpr```プリミティブを使います。

```latex
\the\numexpr〈算術式〉\relax
```

なお、```\the```は```\numexpr```の値を展開するため、```\relax```は```\numexpr```の読み込み終端を示すために付与しています（```\relax```自体は本来「何もしない」コマンド）。

これを使って```\setcounter```の中身を書きなおせば、

```latex
\setcounter{_proof}{\the\numexpr\getrefnumber{theorem:〈ラベル〉}-1\relax}
```
となります。

さあ、これを```\begin{_proof}```の直前に入れればようやく完成です。
```\setcounter```を入れて書き直した```proof```環境の定義は以下のようになります（〈ラベル〉の部分に該当するのは第2引数）。

```latex
% 証明環境：proof
\NewDocumentEnvironment{proof}{O{} d()}%
{\IfNoValueTF{#2}{\begin{_proof*}[#1]}{\setcounter{_proof}{\the\numexpr\getrefnumber{theorem:#2}-1\relax}\begin{_proof}[#1]}}%開始コマンド
{\IfNoValueTF{#2}{\end{_proof*}}{\label{proof:#2}\end{_proof}}}%終了コマンド
```

## 完成品

ここまで長らくお疲れ様でした。こちらが完成品になります。

```latex
% 定理環境（内部用）：_theorem, _theorem*
\newtheoremstyle{mytheorem}{}{}{}{}{\bfseries}%
{\\}{0.5zw}{\underline{\thmname{#1}\thmnumber{#2}\thmnote{\hspace{0.5em}#3}：}}% 1. 新しいスタイルを作る
\theoremstyle{mytheorem}% 2. 上記スタイルを有効にする
\newtheorem{_theorem}{定理}% 3. 有効になっているスタイルの環境を作る．
\newtheorem*{_theorem*}{定理}% 3. 〃　（←番号なし環境を作るコマンド）
% 証明環境（内部用）：_proof, _proof*
\newtheoremstyle{myproof}{}{}{}{}{\bfseries}%
{\\}{0.5zw}{\underline{\thmnumber{定理#2の}\thmname{#1}\thmnote{\hspace{0.5em}（#3）}：}}% 1. 新しいスタイルを作る
\theoremstyle{myproof}% 2. 上記スタイルを有効にする
\newtheorem{_proof}{証明}% 3. 有効になっているスタイルの環境を作る．
\newtheorem*{_proof*}{証明}% 3. 〃　（←番号なし環境を作るコマンド）
% 定理環境：theorem
\NewDocumentEnvironment{theorem}{O{} d()}%
{\IfNoValueTF{#2}{\begin{_theorem*}[#1]}{\begin{_theorem}[#1]}}%開始コマンド
{\IfNoValueTF{#2}{\end{_theorem*}}{\label{theorem:#2}\end{_theorem}}}%終了コマンド
% 証明環境：proof
\RenewDocumentEnvironment{proof}{O{} d()}%
{\IfNoValueTF{#2}{\begin{_proof*}[#1]}{\setcounter{_proof}{\the\numexpr\getrefnumber{theorem:#2}-1\relax}\begin{_proof}[#1]}}%開始コマンド
{\IfNoValueTF{#2}{\end{_proof*}}{\label{proof:#2}\end{_proof}}}%終了コマンド
% 参照用コマンド
\newcommand{\thoremref}[1]{定理\ref{theorem:#1}}
\newcommand{\proofref}[1]{定理\ref{proof:#1}の証明}
```

## 参考サイト

本記事を書くにあたって、下記のサイトを参考にしました。ありがとうございます。

* amsthmについて
    - http://d.hatena.ne.jp/abenori/20141207#1417977311
    - http://mirrors.ctan.org/macros/latex/required/amslatex/amscls/doc/amsthdoc.pdf
* xparseについて
    - http://qiita.com/zr_tex8r/items/50168ad7087516c3e139
    - http://mirrors.ctan.org/macros/latex/contrib/l3packages/xparse.pdf
* refcountについて
    - http://tex.stackexchange.com/questions/120202/use-a-ref-value-to-set-a-counter-value
    - http://mirrors.ctan.org/macros/latex/contrib/oberdiek/refcount.pdf
* ```\numexpr```について
    - http://tex.stackexchange.com/questions/210450/print-counter-value-minus-1