检查浮点数的引用顺序

检查浮点数的引用顺序

我想确保引用符合浮点数出现的顺序。也就是说,我不想改变浮点数的顺序,但我想避免这种情况:

... 如图 2 所示。...如图 1 所示。

如果第一次引用的图表或表格的编号高于以下编号,我该如何发出警告?当然需要否决此案

...如图 1 所示。 ...如图 2 所示。 ...如图 1 所示。

答案1

我建议在每次引用时检查所引用图形的编号,看看它是否是最高的,如果不是,检查它是否之前被引用过。否则:抛出错误。

请考虑以下文档:

\documentclass{scrartcl}

\usepackage{hyperref}
\usepackage[demo]{graphicx}

\begin{document}

\section{Figures}\label{sec}

\begin{figure}
\includegraphics[width=4cm,height=2cm]{}
\caption{first figure}
\label{first}
\end{figure}

\autoref{first}
\autoref{second}\autoref{sec}\autoref{first}

\begin{figure}
\includegraphics[width=2cm,height=4cm]{}
\caption{second figure}
\label{second}
\end{figure}

\end{document}

这导致

代码编译

如果我没看错的话,这应该没问题,但是删除第一的 \autoref{first}会导致错误。

为了实现这一点,请注意hyperref\r@<labelname>处理标签:对于每个标签,定义一个由 5 位信息组成的命令,在本例中(对于标签first):

> \r@first=macro:
->{1}{1}{first figure}{figure.1}{}.

对我们来说重要的是:第四组由 组成<counter name>.<counter value>。这可以通过定义来提取,例如,

\def\parse#1#2#3#4#5{\@parse#4\@nil}
\def\@parse#1.#2\@nil{\def\current@type{#1}\def\current@number{#2}}

现在呼叫

\expandafter\parse\r@first

figure\current@type1中保存\current@number。但请注意,\r@<label>不是在第一次运行 LaTeX 时定义,因为它的定义\newlabel存在于aux文件中。因此,任何使用它都应该包含在测试中,以查看命令是否已定义!

接下来,我们引入一个计数器来跟踪已被引用的数字:

\newcounter{highestyet}
\setcounter{highestyet}{0}

每次调用\autoref都应将当前值与该值进行对比。让我们收集我们想要\autoref在某个命令中执行的所有操作,并使用一个参数作为我们引用的标签名称:

首先,我们应该检查是否\r@#1已定义。如果是,我们使用\parse宏来提取计数器名称和值,否则我们将它们设置为0以避免错误:

  \ifcsname r@#1\endcsname%
     \expandafter\expandafter\expandafter\parse%
         \csname r@#1\endcsname%
  \else\def\current@type{0}\def\current@number{0}\fi%

etoolbox包提供了测试\ifdefstring,我们可以使用它来查看计数器名称是否为figure。在这种情况下,我们可以使用\ifnumgreater当前值与迄今为止看到的最高值进行比较。如果\current@number大于,highestyet我们将替换它。如果它较小,我们检查这个图是否已经被引用。这个想法是为每个图号设置一个布尔值,一旦它被引用,就设置为true。为此,我们可以使用\providebool命令(再次来自etoolbox) 使我们在检查布尔值之前不必担心布尔值是否 (未) 定义。如果布尔值为 false,我们会发出警告。否则,什么也不会发生:

  \ifdefstring{\current@type}{figure}{%
    \ifnumgreater{\current@number}{\value{highestyet}}
      {\setcounter{highestyet}{\current@number}}
      {\ifnumgreater{\value{highestyet}}{\current@number}
        {\expandafter\providebool{figure@\current@number @isrefd}%
         \expandafter\ifbool{figure@\current@number @isrefd}
            {}{Warning!}}{}}%
    \expandafter\providebool{figure@\current@number @isrefd}%
    \expandafter\booltrue{figure@\current@number @isrefd}%
  }{}%

好的,就是这样!如果我们调用这个宏\stuff,我们需要做的就是附加\stuff到(或您选择的参考命令...但请注意,命令的\autoref结构在解析宏中明确使用,如果您使用任何其他标签包,您可能需要检查它们没有重新定义它!)。hyperref\newlabel

然而,直接修补是困难的\autoref。仔细观察(\show\xshowcmdxpatch是你的朋友在这里...),\autoref实际上调用\HyRef@autoref,这是实际与我们感兴趣的参数(标签名称)一起工作的命令(请注意,标签存储在#2!):

> \autoref =macro:
->\leavevmode \@ifstar {\HyRef@autoref \@gobbletwo }{\HyRef@autoref \hyper@@link }.
> \HyRef@autoref=macro:
#1#2->\begingroup \Hy@safe@activestrue \expandafter \HyRef@autosetref \csname r@#2\endcsname {#2}{#1}\endgroup .

我们可以简单地修补它,\xapptocmd使用xpatch包裹:

\xapptocmd{\HyRef@autoref}{\stuff{#2}}{}{}

事实上,在这种情况下,\apptocmd来自etoolbox如果你正在使用一个没有l3(因此没有) 的旧系统,也可以解决这个问题xpatch) 支持。

对于您希望遵守此行为的任何其他命令也是如此:例如,当使用cleveref包(应该加载 hyperref!),可以检查所有变体\cref(例如\Cref)最终都会调用\@cref,它将调用的命令和标签名称作为参数,并将后者再次存储在中#2。因此,如上所述,应该添加

\xapptocmd{\@cref}{\stuff{#2}}{}{}

到文件(与上述相同的注释适用于x)。由于它们使用相同的计数器,因此命令也将协作,即先前的引用使用\autocite也将由后续的检测到\cref,等等。

现在,完整文档

\documentclass{scrartcl}

\usepackage[demo]{graphicx}
\usepackage{xpatch}
\usepackage{hyperref}

\makeatletter
\def\parse#1#2#3#4#5{\@parse#4\@nil}
\def\@parse#1.#2\@nil{\def\current@type{#1}\def\current@number{#2}}

\newcounter{highestyet}
\setcounter{highestyet}{0}

\def\stuff#1{%
  \ifcsname r@#1\endcsname%
     \expandafter\expandafter\expandafter\parse%
         \csname r@#1\endcsname%
  \else\def\current@type{0}\def\current@number{0}\fi%
  \ifdefstring{\current@type}{figure}{%
    \ifnumgreater{\current@number}{\value{highestyet}}
      {\setcounter{highestyet}{\current@number}}
      {\ifnumgreater{\value{highestyet}}{\current@number}
        {\expandafter\providebool{figure@\current@number @isrefd}%
         \expandafter\ifbool{figure@\current@number @isrefd}
            {}{Warning!}}{}}%
    \expandafter\providebool{figure@\current@number @isrefd}%
    \expandafter\booltrue{figure@\current@number @isrefd}%
  }{}%
}

\xapptocmd{\HyRef@autoref}{\stuff{#2}}{}{}
\makeatother

\begin{document}

\section{Figures}\label{sec}

\begin{figure}
\includegraphics[width=4cm,height=2cm]{}
\caption{first figure}
\label{first}
\end{figure}

%\autoref{first}
\autoref{second}\autoref{sec}\autoref{first}

\begin{figure}
\includegraphics[width=2cm,height=4cm]{}
\caption{second figure}
\label{second}
\end{figure}

\end{document}

编译为

第二份文件的汇编

如果删除行中的注释%\autoref{first},结果与上面相同(即警告消失)。

在实践中,你可能希望用类似以下内容替换“警告!”

\GenericWarning{}{Warning! Figure \current@number\space referenced after
     figure \thehighestyet\space on page \thepage\space without being
     referenced before}%

生产

Warning! Figure 1 referenced after figure 2 on page 1 without being referenced 
before on input line 45.

(也可以看看errmessage 中的命令扩展

最后要说明的是:这种方法当然适用于更多的计数器figure。例如,要包含表格,需要区分计数器highestyet,即将它们命名为highestyet@figure和,highestyet@table并添加

\ifdefstring{\current@type}{table}

检查宏中false的部分。这应该包括与上面相同的代码,并在适当的位置设置适当的计数器。\ifdefstring{\current@type}{figure}\stuff

答案2

这只是 Jonathans 代码的更新版本,它包含针对软件包用户的小错误修复caption,并且针对表格进行了扩展。

这也可作为如何在fancyref包中使用此功能的示例。

\documentclass{scrartcl}

\usepackage[demo]{graphicx}
\usepackage{xpatch}
\usepackage{fancyref}
\usepackage{hyperref}
\usepackage{caption}


\makeatletter
\def\parse#1#2#3#4#5{\@parse#4\@nil}

%%Begin fix for caption package: %%
\def\@parse#1.#2\@nil{
  \def\current@type{#1}
  \ifdefstring{\current@type}{figure}{\@@parse#2\@nil}
    {\ifdefstring{\current@type}{table}{\@@parse#2\@nil}}
    {}
  }
\def\@@parse#1.#2\@nil{\def\current@number{#2}}
%%End fix for caption package: %%
%If you ar not using \usepackage{caption} then simply use this line instead:
%\def\@parse#1.#2\@nil{\def\current@type{#1}\def\current@number{#2}}


\newcounter{highestfigureyet}
\setcounter{highestfigureyet}{0}

\newcounter{highesttableyet}
\setcounter{highesttableyet}{0}


\def\checkfloatreforder#1{%
  \ifcsname r@#1\endcsname%
     \expandafter\expandafter\expandafter\parse%
         \csname r@#1\endcsname%      
  \else\def\current@type{0}\def\current@number{0}\fi%
  \ifdefstring{\current@type}{figure}{%
    \ifnumgreater{\current@number}{\value{highestfigureyet}}
      {\setcounter{highestfigureyet}{\current@number}}
      {\ifnumgreater{\value{highestfigureyet}}{\current@number}
        {\expandafter\providebool{figure@\current@number @isrefd}%
         \expandafter\ifbool{figure@\current@number @isrefd}
            {}{\GenericWarning{}{Warning! Figure \current@number\space referenced after
     figure \thehighestfigureyet\space on page \thepage\space without being referenced before}}}{}}%
    \expandafter\providebool{figure@\current@number @isrefd}%
    \expandafter\booltrue{figure@\current@number @isrefd}%
  }{%
    \ifdefstring{\current@type}{table}{%
      \ifnumgreater{\current@number}{\value{highesttableyet}}
        {\setcounter{highesttableyet}{\current@number}}
        {\ifnumgreater{\value{highesttableyet}}{\current@number}
          {\expandafter\providebool{table@\current@number @isrefd}%
           \expandafter\ifbool{table@\current@number @isrefd}
              {}{\GenericWarning{}{Warning! Table \current@number\space referenced after
     table \thehighesttableyet\space on page \thepage\space without being referenced before}}}{}}%
      \expandafter\providebool{table@\current@number @isrefd}%
      \expandafter\booltrue{table@\current@number @isrefd}%
      }{}  
  }%
}

\let\old@fref\fref
\renewcommand*{\fref}[1]{\old@fref{#1}
\checkfloatreforder{#1}}

\makeatother


\begin{document}

\section{Figures}\label{sec:figures}

\begin{figure}
\includegraphics[width=4cm,height=2cm]{}
\caption{first figure}
\label{fig:first}
\end{figure}

\fref{fig:second} in \fref{sec:figures} and \fref{fig:first}


\begin{figure}
\includegraphics[width=2cm,height=4cm]{}
\caption{second figure}
\label{fig:second}
\end{figure}

\section{Tables}\label{sec:tables}

\begin{table}
\includegraphics[width=4cm,height=2cm]{}
\caption{first table}
\label{tab:first}
\end{table}

\fref{tab:second} in \fref{sec:tables} and \fref{tab:first}


\begin{table}
\includegraphics[width=2cm,height=4cm]{}
\caption{second table}
\label{tab:second}
\end{table}

\end{document}

造成这一切的原因是,caption包改变了您必须解析的字符串:

  • {1}{1}{first figure\relax }{figure.1}{}更改为

    {1}{1}{first figure\relax}{figure.caption.1}{}

    这也适用于表格

  • 各部分的列表项标签保持不变:

    {1}{1}{Figures}{section.1}{}遗迹

    {1}{1}{Figures}{section.1}{}

相关内容