我正在学习如何使用 thmtools 包,遇到了一个奇怪的怪癖。一个简单的例子如下:
\documentclass{article}
\usepackage{amsthm}
\usepackage{thmtools}
\declaretheorem{theorem}
\begin{document}
\begin{restatable*}{theorem}{mythm}
This is a restated theorem.
\end{restatable*}
\begin{theorem}
This is a regular theorem.
\end{theorem}
\begin{theorem}
This is a regular theorem.
\end{theorem}
\mythm
\mythm
\mythm
\begin{theorem}
This is a regular theorem.
\end{theorem}
\begin{theorem}
This is a regular theorem.
\end{theorem}
\begin{theorem}
This is a regular theorem.
\end{theorem}
\mythm*
\mythm*
\mythm*
\end{document}
当我编译此代码时,重新陈述和重新编号的定理(\mythm 命令)上方有多余的空间。这些定理下方没有多余的空间(如您在定理 5 和 6 之间看到的)。\mythm* 命令不会创建任何多余的空间。
实际上,restatable* 环境将用于在论文开头陈述结果。只会有一个附带的 \mythm 命令(并且可能有任意数量的 \mythm* 命令)。因此,这个额外的空间只会成为一次问题。不过,我想知道这里发生了什么。
答案1
我在宏定义中添加了一行\thmt@rst@storecounters
:硬连线\vspace.
它可能不漂亮,但可以完成工作。(已编辑以修复较小的例程,从而占用更少的空间。原始帖子已将相同的修复应用于更长的环境thmt@restatable
。)
\documentclass{article}
\usepackage{amsthm}
\usepackage{thmtools}
\makeatletter
\def\thmt@rst@storecounters#1{%
%THIS IS THE LINE I ADDED:
\vspace{-1.9ex}%
\bgroup
% ugly hack: save chapter,..subsection numbers
% for equation numbers.
%\refstepcounter{thmt@dummyctr}% why is this here?
%% temporarily disabled, broke autorefname.
\def\@currentlabel{}%
\@for\thmt@ctr:=\thmt@innercounters\do{%
\thmt@sanitizethe{\thmt@ctr}%
\protected@edef\@currentlabel{%
\@currentlabel
\protect\def\@xa\protect\csname the\thmt@ctr\endcsname{%
\csname the\thmt@ctr\endcsname}%
\ifcsname theH\thmt@ctr\endcsname
\protect\def\@xa\protect\csname theH\thmt@ctr\endcsname{%
(restate \protect\theHthmt@dummyctr)\csname theH\thmt@ctr\endcsname}%
\fi
\protect\setcounter{\thmt@ctr}{\number\csname c@\thmt@ctr\endcsname}%
}%
}%
\label{thmt@@#1@data}%
\egroup
}%
\makeatother
\declaretheorem{theorem}
\begin{document}
\begin{restatable*}{theorem}{mythm}
This is a restated theorem.
\end{restatable*}
\begin{theorem}
This is a regular theorem.
\end{theorem}
\begin{theorem}
This is a regular theorem.
\end{theorem}
\mythm
\mythm
\mythm
\begin{theorem}
This is a regular theorem.
\end{theorem}
\begin{theorem}
This is a regular theorem.
\end{theorem}
\begin{theorem}
This is a regular theorem.
\end{theorem}
\mythm*
\mythm*
\mythm*
\end{document}
答案2
您的解决方案的问题在于,restatable 仅在其前面有另一个环境时才会引入额外空格。在其他情况下,空格是正确的,应避免使用 \vspace。
通过多次实验,似乎罪魁祸首是\label{thmt@@#1@data}
in \thmt@rst@storecounters
。通过删除该\label
命令,间距是正确的,但其他东西可能会出错。我的解决方案(这些天我正在测试)是将调用移至\thmt@rst@storecounters
in 。这是我现在正在测试\thmt@restatable
的版本:\thmt@restatable
\renewenvironment{thmt@restatable}[3][]{%
\thmt@toks{}% will hold body
\stepcounter{thmt@dummyctr}% used for data storage label.
\long\def\thmrst@store##1{%
\@xa\gdef\csname #3\endcsname{%
\@ifstar{%
\thmt@thisistheonefalse\csname thmt@stored@#3\endcsname
}{%
\thmt@thisistheonetrue\csname thmt@stored@#3\endcsname
}%
}%
\@xa\long\@xa\gdef\csname thmt@stored@#3\@xa\endcsname\@xa{%
\begingroup
\ifthmt@thisistheone
% nothing here in my patched version
\else
% this one should use other numbers...
% first, fake the theorem number.
\@xa\protected@edef\csname the#2\endcsname{%
\thmt@trivialref{thmt@@#3}{??}}%
% if the number wasn't there, have a "re-run to get labels right"
% warning.
\ifcsname r@thmt@@#3\endcsname\else
\G@refundefinedtrue
\fi
% prevent stepcountering the theorem number,
% but still, have some number for hyperref, just in case.
\@xa\let\csname c@#2\endcsname=\c@thmt@dummyctr
\@xa\let\csname theH#2\endcsname=\theHthmt@dummyctr
% disable labeling.
\let\label=\@gobble
\let\ltx@label=\@gobble% amsmath needs this
% We shall need to restore the counters at the end
% of the environment, so we get
% (4.2) [(3.1 from restate)] (4.3)
\def\thmt@restorecounters{}%
\@for\thmt@ctr:=\thmt@innercounters\do{%
\protected@edef\thmt@restorecounters{%
\thmt@restorecounters
\protect\setcounter{\thmt@ctr}{\arabic{\thmt@ctr}}%
}%
}%
% pull the new semi-static definition of \theequation et al.
% from the aux file.
\thmt@trivialref{thmt@@#3@data}{}%
\fi
% call the proper begin-env code, possibly with optional argument
% (omit if stored via key-val)
\ifthmt@restatethis
\thmt@restatethisfalse
\else
\csname #2\@xa\endcsname\ifx\@nx#1\@nx\else[{#1}]\fi
\fi
\ifthmt@thisistheone
% these are the valid numbers, store them for the other
% occasions.
\thmt@rst@storecounters{#3}%
% store a label so we can pick up the number later.
\label{thmt@@#3}%
\fi
% this will be the collected body.
##1%
\csname end#2\endcsname
% if we faked the counter values, restore originals now.
\ifthmt@thisistheone\else\thmt@restorecounters\fi
\endgroup
}% thmt@stored@#3
% in either case, now call the just-created macro,
\csname #3\@xa\endcsname\ifthmt@thisistheone\else*\fi
% and artificially close the current environment.
\@xa\end\@xa{\@currenvir}
}% thm@rst@store
\thmt@collect@body\thmrst@store
}{%
%% now empty, just used as a marker.
}
答案3
有一种简单的方法可以解决这个问题,虽然不是那么普遍,但(在我看来)是最自然的情况,即定理一出现就会得到一个数字,并且所有重述都有相同的数字。所以情况是:
\documentclass{article}
\usepackage{amsthm}
\usepackage{thmtools}
\declaretheorem{theorem}
\begin{document}
\begin{theorem}
This is a regular theorem.
\end{theorem}
\begin{restatable}{theorem}{mythm}
This is a restated theorem.
\end{restatable}
\begin{theorem}
This is a regular theorem.
\end{theorem}
\begin{theorem}
This is a regular theorem.
\end{theorem}
\mythm*
\mythm*
\mythm*
\begin{theorem}
This is a regular theorem.
\end{theorem}
\begin{theorem}
This is a regular theorem.
\end{theorem}
\begin{theorem}
This is a regular theorem.
\end{theorem}
\mythm*
\mythm*
\mythm*
\end{document}
我使用 而\begin{theorem}[restated = mythm, name = ]
不是 来修复它\begin{restatable}{theorem}{mythm}
。
\documentclass{article}
\usepackage{amsthm}
\usepackage{thmtools}
\declaretheorem{theorem}
\begin{document}
\begin{theorem}
This is a regular theorem.
\end{theorem}
% If you have only restate argument then the name argument value will be , (comma),
% so you need to specify any other argument
\begin{theorem}[restate = mythm, name = ]\label{Thm:my}
This is a restated theorem.
\end{theorem}
\begin{theorem}
This is a regular theorem.
\end{theorem}
\begin{theorem}
This is a regular theorem.
\end{theorem}
\mythm*
\mythm*
\mythm*
\begin{theorem}
This is a regular theorem.
\end{theorem}
\begin{theorem}
This is a regular theorem.
\end{theorem}
\begin{theorem}
This is a regular theorem.
\end{theorem}
\mythm*
\mythm*
\mythm*
\end{document}
答案4
总结:etoolbox
这是一个依赖于和的修复hyperref
。
\usepackage{etoolbox}
\usepackage{hyperref}
\usepackage{thm-restate}
\makeatletter
\pretocmd{\thmt@rst@storecounters}{\Hy@SaveLastskip}{}{}
\apptocmd{\thmt@rst@storecounters}{\Hy@RestoreLastskip}{}{}
\makeatother
etoolbox
请参阅下文以了解不依赖于额外包的(更长)版本hyperref
以及完整的工作示例。
正如 Gianlu 所指出的,问题在于\label
。\thmt@rst@storecounters
这是因为基本上两个定理(或证明、引理等)之间的任何内容都会弄乱定理间的间距。
这个问题和答案是关于同一个问题的。答案建议使用hyperref
包中的两个内部宏:\Hy@SaveLastskip
和\Hy@RestoreLastskip
。为了解决这个问题,我们要把放在\Hy@SaveLastskip
前面\label
和\Hy@RestoreLastskip
后面。
上述代码加载了hyperref
,它提供了前面提到的宏,和etoolbox
,它提供了一种将它们添加到 的开始(pretocmd
)和 的结束(apptocmd
)的方法,而\thmt@rst@storecounters
无需复制整个定义。
可以通过以下方式避免使用额外的包:(1)从复制\Hy@SaveLastskip
和的定义,以及 (2) 手动重新定义,主要是从 复制原始内容。如下所示。\Hy@RestoreLastskip
hyperref
\thmt@rst@storecounters
thm-restate
% Instead of loading `hyperref`
\def\Hy@SaveLastskip{%
\let\Hy@RestoreLastskip\relax
\ifvmode
\ifdim\lastskip=\z@
\ifnum\lastnodetype=1 %
\let\Hy@RestoreLastskip\relax
\else
\let\Hy@RestoreLastskip\nobreak
\fi
\else
\begingroup
\skip@=-\lastskip
\edef\x{%
\endgroup
\def\noexpand\Hy@RestoreLastskip{%
\noexpand\ifvmode
\noexpand\nobreak
\vskip\the\skip@
\vskip\the\lastskip\relax
\noexpand\fi
}%
}%
\x
\fi
\else
\ifhmode
\ifdim\lastskip=\z@
\let\Hy@RestoreLastskip\nobreak
\else
\begingroup
\skip@=-\lastskip
\edef\x{%
\endgroup
\def\noexpand\Hy@RestoreLastskip{%
\noexpand\ifhmode
\noexpand\nobreak
\hskip\the\skip@
\hskip\the\lastskip\relax
\noexpand\fi
}%
}%
\x
\fi
\fi
\fi
}
% Instead of loading `etoolbox`
\def\thmt@rst@storecounters#1{%
%%% NEW ADDITION:
\Hy@SaveLastskip
\bgroup
\def\@currentlabel{}%
\@for\thmt@ctr:=\thmt@innercounters\do{%
\thmt@sanitizethe{\thmt@ctr}%
\protected@edef\@currentlabel{%
\@currentlabel
\protect\def\@xa\protect\csname the\thmt@ctr\endcsname{%
\csname the\thmt@ctr\endcsname}%
\ifcsname theH\thmt@ctr\endcsname
\protect\def\@xa\protect\csname theH\thmt@ctr\endcsname{%
(restate \protect\theHthmt@dummyctr)\csname theH\thmt@ctr\endcsname}%
\fi
\protect\setcounter{\thmt@ctr}{\number\csname c@\thmt@ctr\endcsname}%
}%
}%
\label{thmt@@#1@data}%
\egroup
%%% NEW ADDITION:
\Hy@RestoreLastskip
}
\documentclass{article}
\usepackage{amsthm}
\usepackage{thmtools}
\usepackage{hyperref}
\usepackage{etoolbox}
\declaretheorem{theorem}
\makeatletter
\newcommand{\fixit}{%
\pretocmd{\thmt@rst@storecounters}{\Hy@SaveLastskip}{}{}
\apptocmd{\thmt@rst@storecounters}{\Hy@RestoreLastskip}{}{}
}
\makeatother
\newcommand{\demo}{%
\begin{restatable*}{theorem}{mythm}
This is a restated theorem.
\end{restatable*}
\begin{theorem}
This is a regular theorem.
\end{theorem}
\begin{theorem}
This is a regular theorem.
\end{theorem}
\mythm
\mythm
\mythm
\begin{theorem}
This is a regular theorem.
\end{theorem}
\begin{theorem}
This is a regular theorem.
\end{theorem}
\begin{theorem}
This is a regular theorem.
\end{theorem}
\mythm*
\mythm*
\mythm*
}
\begin{document}
% Original
\demo
% Implement the patch
\fixit
% Visual separation between original and fixed demos
\bigskip\hrule\bigskip
% Reset theorem counter so the two demos use the same numbers
\setcounter{theorem}{0}
% Fixed
\demo
\end{document}