我有一个小的自定义样式文件/包,其中包含一堆对我所从事的计算机科学子领域有用的宏(请参阅这里以获取软件包的最新完整版本)。我编写的大部分内容涉及制定编号“规则”。为此,我使用array
,包裹在 a 中newenvironment
(它本身被包裹在 a 中newfloat
以获得浮动行为,但我怀疑这与此无关)。
每条规则本身都是使用另一个宏写出的,以确保它们都以相同的方式指定,并为我提供规则的自动顺序编号。我希望能够像 LaTeX 中许多其他类型的可数事物一样标记和交叉引用规则。我尝试为规则宏包含一个可选参数,并label
在可选参数非空时写入(您可以在下面的 MWE 中看到这一点)。当我尝试创建一个最小的工作示例时,这似乎一开始效果很好,但当我将它应用于我完成的一些旧论文时,我没有看到相同的行为。相反,我收到了我不太理解的错误消息。
最终,经过几个小时尝试让 MWE 和一篇旧论文产生相同的输出后,我发现问题出现与 amsmath 一致。具体来说,当 amsmath 通过 usepackage 包含时,它不喜欢我尝试自定义标签。我猜这与规则环境使用 的工作方式有关array
,并且 amsmatharray
以某种方式重新定义,使标签停止工作。鉴于 amsmath 几乎肯定会被纳入我的软件包可能相关的任何论文中,这显然不是可接受的情况。
MWE 如下所示,它或多或少是通过从 .sty 文件中复制粘贴相关部分并将其包装在makeatletter
/中构建的makeatother
。我还使用 包含了 sty 文件中包含的所有包RequirePackage
,以防万一出现任何差异。
\documentclass[10pt,a4paper]{article}
\usepackage{amsmath,newfloat,array,framed,etoolbox,perfectcut,trimspaces,changepage}
\usepackage{hyperref}
\usepackage{cleveref}
\makeatletter
\newcounter{cpsystems@RuleNum}
\DeclareFloatingEnvironment[name=Ruleset,within=none]{cprulesetfloat}
\newenvironment{cpruleset}
{\begin{framed}\begin{adjustwidth}{-1.0em}{-1.0em}
\renewcommand{\arraystretch}{1.1}\[\begin{array}{lllllr}}
{\end{array}\]\end{adjustwidth}\end{framed}}
\newcommand{\cprule}[6][]{
\refstepcounter{cpsystems@RuleNum}
\notblank{#1}{\label{#1}}{}
\cpsystems@basecprule{#2}{#3}{#4}{#5}{#6}{\hspace{1.5em}(\arabic{cpsystems@RuleNum})}
}
\newcommand{\cpsystems@basecprule}[6]{
\trim@spaces@noexp{#1 & #2 & \rightarrow_{#3} & #4 & #5 & #6\\}
}
\newcommand*{\cpfunc}[2]{
\trim@spaces@noexp{#1\perfectunary{IncreaseHeight}{(}{)}{#2}}
}
\makeatother
\begin{document}
\section{Intro}
See Ruleset~\ref{rules}.
\section{The rules}
\begin{cprulesetfloat}
\begin{cpruleset}
\cprule[rule1]{s_1}{\cpfunc{a}{B}}{{\scriptstyle 1}}{s_2}{\cpfunc{f}{G}}
\cprule{s_2}{\cpfunc{c}{D}}{{\scriptstyle +}}{s_3}{\cpfunc{h}{I}}
\cprule[rule2]{s_2}{\cpfunc{c}{D}}{{\scriptstyle +}}{s_3}{\cpfunc{h}{I}}
\end{cpruleset}
\caption{\label{rules}My fancy ruleset}
\end{cprulesetfloat}
I'm particularly proud of rule~\ref{rule3}. That's \ref{rule3}.
\begin{cprulesetfloat}
\begin{cpruleset}
\cprule[rule3]{s_1}{\cpfunc{a}{B}}{{\scriptstyle 1}}{s_2}{\cpfunc{f}{G}}
\cprule{s_2}{\cpfunc{c}{D}}{{\scriptstyle +}}{s_3}{\cpfunc{h}{I}}
\cprule{s_2}{\cpfunc{c}{D}}{{\scriptstyle +}}{s_3}{\cpfunc{h}{I}}
\end{cpruleset}
\caption{\label{rules2}My fancy ruleset 2}
\end{cprulesetfloat}
\end{document}
MWE 有两个问题。首先,如果第一个中的两个可选参数(预期标签)cpruleset
都保留,则会出现编译错误。这似乎是 amsmath 的问题 - 它显然认为它们都是数组的标签或其他东西,因此抱怨为同一事物定义了多个标签。其次,出于我完全不清楚的原因,无论我尝试使用什么命令ref
来引用规则(确保每个只有一个标签后cpruleset
),它总是返回数字 2。
cpruleset
我的问题是:我应该如何更改环境和/或宏的定义cprule
,以启用规则编号的标记和交叉引用?我已经尝试了所有能想到的方法,但此时我完全不知所措。我最好的猜测是我需要从移动到不同的环境array
,但尽管四处寻找了一下(例如,align
在gather
amsmath 和 mathtools 包中,等等),我找不到任何似乎能够让我在不考虑交叉引用的情况下实现相同结果的东西。最好但不是至关重要的是,任何解决方案都可以与 hyperref 和/或 cleveref 很好地配合使用,并可以启用类似\autoref{rule2}
或\cref{rule2}
获得类似于“规则 2”的输出。
答案1
amsmath 重新定义,\label
因此如果\label
显示数学中有两个命令,它就会发出抱怨。
它存储原始\label
命令,以便\ltx@label
您可以使用它---但不能使用 cleveref,因为 cleveref 也重新定义\label
(在开始文档时,基于 amsmath 定义),然后您会收到许多关于缺失和多个标签的警告。
为了解决这个问题,您可以根据 zref 定义自己的 \ruleref 命令:
\documentclass[10pt,a4paper]{article}
\usepackage{amsmath,newfloat,array,framed,etoolbox,perfectcut,trimspaces,changepage}
\usepackage{hyperref}
\usepackage{cleveref}
\usepackage[user]{zref}
\makeatletter
\zref@newprop{destname}[Doc-Start]{\@currentHref}
\zref@addprop{main}{destname}
\newcommand\ruleref[1]{\hyperlink{\zref@extract{#1}{destname}}{\zref{#1}}}
\newcounter{cpsystems@RuleNum}
\DeclareFloatingEnvironment[name=Ruleset,within=none]{cprulesetfloat}
\newenvironment{cpruleset}
{\begin{framed}\begin{adjustwidth}{-1.0em}{-1.0em}
\renewcommand{\arraystretch}{1.1}\[\begin{array}{lllllr}}
{\end{array}\]\end{adjustwidth}\end{framed}}
\newcommand{\cprule}[6][]{
\refstepcounter{cpsystems@RuleNum}
\notblank{#1}{\zlabel{#1}}{}
\cpsystems@basecprule{#2}{#3}{#4}{#5}{#6}{\hspace{1.5em}(\arabic{cpsystems@RuleNum})}
}
\newcommand{\cpsystems@basecprule}[6]{
\trim@spaces@noexp{#1 & #2 & \rightarrow_{#3} & #4 & #5 & #6\\}
}
\newcommand*{\cpfunc}[2]{
\trim@spaces@noexp{#1\perfectunary{IncreaseHeight}{(}{)}{#2}}
}
\makeatother
\begin{document}
\section{Intro}
See Ruleset~\ref{rules}.
\section{The rules}
\begin{cprulesetfloat}
\begin{cpruleset}
\cprule[rule1]{s_1}{\cpfunc{a}{B}}{{\scriptstyle 1}}{s_2}{\cpfunc{f}{G}}
\cprule{s_2}{\cpfunc{c}{D}}{{\scriptstyle +}}{s_3}{\cpfunc{h}{I}}
\cprule[rule2]{s_2}{\cpfunc{c}{D}}{{\scriptstyle +}}{s_3}{\cpfunc{h}{I}}
\end{cpruleset}
\caption{\label{rules}My fancy ruleset}
\end{cprulesetfloat}
I'm particularly proud of rule~\ruleref{rule3}. That's \ruleref{rule3}.
\begin{cprulesetfloat}
\begin{cpruleset}
\cprule[rule3]{s_1}{\cpfunc{a}{B}}{{\scriptstyle 1}}{s_2}{\cpfunc{f}{G}}
\cprule{s_2}{\cpfunc{c}{D}}{{\scriptstyle +}}{s_3}{\cpfunc{h}{I}}
\cprule{s_2}{\cpfunc{c}{D}}{{\scriptstyle +}}{s_3}{\cpfunc{h}{I}}
\end{cpruleset}
\caption{\label{rules2}My fancy ruleset 2}
\end{cprulesetfloat}
\end{document}