2018年4月26日木曜日

定理を再掲したときに定理番号も揃える(amsmathのtagと似た挙動の定理環境を作る).

LaTeXの話題です.amsmath.styで定義されているtagコマンドは,数式環境の式番号部分を自由な文字列で置き換えることができるようになっています.下に例を挙げます.

この画像を生成するLaTeXのコードは次のようになります.
\documentclass[a4paper,dvipdfmx,uplatex]{jsarticle}

\usepackage[dvipdfmx,bookmarks=true,bookmarksnumbered=true,%
,colorlinks=true,linkcolor=blue]{hyperref}%

\usepackage{showkeys}
\usepackage{amsmath}
\numberwithin{equation}{section}


\begin{document}

\section{amsmath.styに含まれるtagコマンドの挙動}

\begin{equation}
 \label{eq:euler}
e^{i\theta}=\cos\theta +i\sin\theta
\end{equation}

\eqref{eq:euler}


\section{amsmath.styに含まれるtagコマンドの挙動の検証}


\begin{equation}
  \label{eq:euler2}
  \tag{\ref{eq:euler}}
e^{i\theta}=\cos\theta +i\sin\theta
\end{equation}


\eqref{eq:euler2}

\end{document}
最初のequation環境にeq:eulerというlabelをつけ,第2のequation環境ではtagコマンドを使って数式番号部分に¥ref{eq:euler}と書くことで,実際に表示される式番号が第1のequation環境のものと同じになっています.この第2のequation環境にはeq:euler2というlabelを付けており,これを¥eqref{eq:euler2}と参照すると表示される番号は第1のequation環境のものであるにも関わらず,リンクをクリックすると第2のequation環境にジャンプします.

このような現象が起こるのは,上記のファイルをタイプセットして得られるauxファイルの記述を見てみるとわかります.以下はauxファイルのリンク作成に関係する部分を抜き出したものです.
\@writefile{toc}{\contentsline {section}{\numberline {1}amsmath.styに含まれるtagコマンドの挙動}{1}{section.1}}
\newlabel{eq:euler}{{1.1}{1}{amsmath.styに含まれるtagコマンドの挙動}{equation.1.1}{}}
\@writefile{toc}{\contentsline {section}{\numberline {2}amsmath.styに含まれるtagコマンドの挙動の検証}{1}{section.2}}
\newlabel{eq:euler2}{{{\ref  {eq:euler}}}{1}{amsmath.styに含まれるtagコマンドの挙動の検証}{equation.2.1}{}}
第1のequation環境のlabelから作成されたnewlabelには,最初の引数にlabel名が,第2の引数には数式番号が入力されています.第2のequation環境のlabelから作成されたnewlabelには,最初の引数にlabel名が入力されているのは同じですが,第2の引数に¥ref{eq:euler}という文字列が入力されていて,これが実行されることで第2のequation環境の数式番号部分が第1のものに置き換わるわけです.newlabelの2番めの引数部分は,labelコマンドを用いた時点での¥@currentlabelの値が入力されるので,これを変更することでrefコマンドを使った文字列を入力することができそうです.

以上のtagの性質は,例えば1つの文書内のある場所に数式を書いたものの,その場所とはかなり離れた部分で頻繁に参照する場合,もう一度同じ数式を書きたいが,数式番号は以前に記述したものと一致させたいとなったときに便利です.

同様に定理環境ついても1つの文書内のある場所で定理を証明し,遠く離れた場所でその定理を頻繁に参照するようなときに,定理を再掲するも定理番号は最初のものと一致させたいという場合があるのではないでしょうか.また定理番号は最初のものを使いますが,リンク先は再掲した定理の部分に飛ぶほうが文書を読みやすいと思います.そこで上記のtagコマンドと同じような挙動をする定理環境をでっち上げてみました.結果は次のようになりました.
showkeys.styを使ってlabel名や参照先を表示させています.hyperrefで使われるlabelを再定義するという力技を使っているため,定理番号を再利用している部分のlabel名はshowkeysでは表示できなくなってしまっています.ソースファイルは以下のようになりました.
\documentclass[a4paper,dvipdfmx,uplatex]{jsarticle}

\usepackage[dvipdfmx,bookmarks=true,bookmarksnumbered=true,%
bookmarkstype=toc,colorlinks=true,linkcolor=blue]{hyperref}%
\usepackage{nameref}

\usepackage{showkeys}
\usepackage{amsmath}
\numberwithin{equation}{section}
\usepackage{tcolorbox}
\usepackage{varwidth} 
\tcbuselibrary{breakable}
\tcbuselibrary{skins}
\tcbuselibrary{xparse} %内部でxparse.styを読み込むのでNewDocumentCommandなどが使える.

%定理環境の定理番号部分の背景色
\definecolor{tcbtitlebackcolor}{RGB}{52,48,39}


%%%定理環境の定義
\newcounter{theoi}
\newcounter{theoii}
\numberwithin{theoi}{section}% numberwithinはamsmathで定義されているので要amsmath.sty
\NewTColorBox{theobox}{o m o}{%oは省略可能な引数、mは必須の引数を表す。
%dは省略可能な引数でで囲まれた部分を引数とする.
%#1=タイトル(省略可), #2=定理環境名, #3=定理番号部分を再利用したいときに\refコマンドを入れる.
enhanced,
coltitle=white,fonttitle=\bfseries\sffamily,
colbacktitle=tcbtitlebackcolor,
colback=white,
extras broken={%
colback=white,
frame empty,
},
breakable=true,
borderline={0.5mm}{0mm}{tcbtitlebackcolor},
leftrule=0pt,rightrule=0pt,
sharp corners=all,
boxsep=1mm,
before skip=3mm,
attach boxed title to top left={yshift*=-\tcboxedtitleheight-2.5mm},
boxed title style={sharp corners=all,toprule=0pt,bottomrule=0pt,leftrule=0pt,rightrule=0pt,
},
IfValueTF={#3}{%3つ目のオプション引数がある場合はタイトルの番号部分を書き換える。
IfNoValueTF={#1}{title={#2~#3.}}{title={#2~#3\,:~{#1}}}%
}%
{%
IfNoValueTF={#1}{title={#2~\thetheoi.}}{title={#2~\thetheoi\,:~{#1}}}%
},%
pad after break=-4.1mm, %breakした後の空きの調整
}

%% nameref.styのlabelコマンドの\@currentlabel部分を再利用するラベルで書き換えたものを
%% \my@labelというコマンドで定義する.
\makeatletter
\newcommand{\my@reuselabel}[1]{
\def\my@label##1{%
  \@bsphack
  \begingroup
    \def\label@name{##1}%
    \label@hook
    \protected@write\@auxout{}{%
      \string\newlabel{##1}{%
      % {\@currentlabel}%
        {#1}% \@currentlabelの部分に再利用するラベルを入れる
        {\thepage}%
        {\@currentlabelname}%
        {\@currentHref}{}%
      }%
    }%
  \endgroup
  \@esphack
}%
\ifNR@showkeys% showkeys.styを読み込んだときの対策
  \def\my@label##1{%
    \@bsphack
    \SK@\SK@@label{##1}%
    \begingroup
      \def\label@name{##1}%
      \label@hook
      \protected@write\@auxout{}{%
        \string\newlabel{##1}{%
 % {\@currentlabel}%
          {#1}% \@currentlabelの部分に再利用するラベルを入れる
          {\thepage}%
          {\@currentlabelname}%
          {\@currentHref}{}%
        }%
      }%
    \endgroup
    \@esphack
  }%
\fi
}

\NewDocumentEnvironment{theorem}{o m d<>}{%NewDocumentEnvironmentはxparse.styで定義されている。
 \IfValueTF{#3}{%
\let\my@@label=\label% \my@@labelに\labelをコピー
\my@reuselabel{#3}% 再利用するlabel名
\let\label=\my@label% labelコマンドを再定義
\my@reuselabel{#3}
\refstepcounter{theoii}
}{\refstepcounter{theoi}}
\begin{theobox}[#1]{#2}[#3]}{\end{theobox}
\IfValueT{#3}{\let\label=\my@@label}% labelコマンドを元に戻す。
}
\makeatother

\NewDocumentEnvironment{theo}{o d<>}{% 再利用するラベルは<>の中に入れる。
\begin{theorem}[#1]{定理}<#2>
}{\end{theorem}}

\NewDocumentEnvironment{prop}{o d<>}
{\begin{theorem}[#1]{命題}<#2>%
}{\end{theorem}}

\NewDocumentEnvironment{lem}{o d<>}
{\begin{theorem}[#1]{補題}<#2>%
}{\end{theorem}}


\NewDocumentEnvironment{coro}{o d<>}
{\begin{theorem}[#1]{系}<#2>%
}{\end{theorem}}




\begin{document}

\section{定理環境の再利用}

\begin{theo}[主定理]
\label{theo}
定理1~\fbox{\texttt{theo}}
\end{theo}

\begin{prop}
\label{prop:1}
命題1
\end{prop}

\begin{lem}
補題1
\end{lem}


\begin{theo}[主定理コピー]<\ref{theo}>
\label{theo:2}
定理2~\fbox{\texttt{theo:2}}
\end{theo}

\begin{coro}[主定理の系]
\label{coro:1}
系1
\end{coro}

定理~\ref{theo}

定理~\ref{theo:2}, 

命題~\ref{prop:1}, 

系~\ref{coro:1}


\pagebreak

\section{定理環境の再利用2}

\begin{prop}
\label{prop:2}
 命題2
\end{prop}

\begin{theo}[主定理コピー]<\ref{theo}>
\label{theo:3}

定理3~\fbox{\texttt{theo:3}}
\end{theo} 

\begin{theo}[主定理コピー]<\ref{theo:2}>
\label{theo:4}

定理4~\fbox{\texttt{theo:4}}
\end{theo} 

命題~\ref{prop:2}

定理~\ref{theo}, 

定理~\ref{theo:2}, 

定理~\ref{theo:3}, 

定理~\ref{theo:4}, 

\end{document}
上のソースファイルでは,hyperref.styのlabelコマンドの定義が書かれているnameref.styの該当部分をそのままコピーし,¥@currentlabel部分をrefコマンドで無理矢理書き換えるという操作を行っています.これで目的の挙動をする定理環境ができたのですが,考えてみればあまり使わないかもしれません.