我创建了一个问题之前关于我的代码,但它太大太模糊,无法正确隔离我的问题(而且代码也发生了变化)。这是我关于非常简单的玩具示例的新版本,使其更精确、更简短。
我尝试创建一个可以使用相同参数调用的函数,如:
\myFunction{foo} some text \myFunction{foo}
但我需要这个函数在第二种情况下给出不同的结果,此外,标签仅在第一种情况下定义(以便“\ref”仅引用命令的第一次调用)。
所需结果:
"Foo is OK and labeled" some text "you have defined foo before, this is not labeled" !
我尝试了几种方法,但都不能给出可靠的结果。结果往往因环境或/和多次编译而不同。
我尝试使用标签机制,因为它集成了一些有用的警告,并且看起来像 \label{foo} 创建了一个变量 r@foo,我写道:
\newcommand{\MyTesting}[1]
{
\ifcsname r@#1\endcsname
Already defined
\else
\label{#1}
\fi
}
这样做的结果是......很奇怪,因为标签似乎在辅助文件(或类似的其他文件)中写入了一个简单的调用,如:
\MyTesting{test}
将通过连续的编译给出:
- 标签可能已更改,重新运行以获取正确的交叉引用
- 没有什么
- 标签可能已更改,重新运行以获取正确的交叉引用
- 没有什么
- ETC...
因此,结果似乎将一个编译变为两个,这不是想要的结果。
但目前来说这并不重要。让我们用以下代码测试一下:
\MyTesting{test} some text \MyTesting{test}
通过连续的编译,我们得到:
- 标签可能已更改,重新运行以获取正确的交叉引用
- 标签“测试”已多次定义
- 标签可能已更改,重新运行以获取正确的交叉引用
- 标签“测试”已多次定义
- ETC...
在这里我不太明白其中的逻辑......即使标签保存在辅助文件中,\MyTesting 开头的测试也应该可以防止多重定义。
答案的奖励标准:函数的调用也应该通过像“图”中的标题这样的环境来保持稳健,而这个环境似乎被评估了两次……
我接受任何关于这个问题的帮助;)
马德里理工学院:
%%%% work with koma-script, should also work on standard classes %%%%
\documentclass{book}
\usepackage[english]{babel}
\usepackage{lmodern}
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usepackage{graphicx} % only for testing
\usepackage{floatrow} % for testing
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\makeatletter
\newcommand{\MyLabel}[1]
{
\ifcsname r@#1\endcsname
Already defined
\else
\label{#1}
\fi
}
\makeatother
%%%%%% begin %%%%%%%
\begin{document}
%%%%%% TEST %%%%%%
\chapter{TEST}
\section{Introduction}
Try to label a first time \MyLabel{Firsttest}
Try to label a second time with the same \MyLabel{Firsttest}
%%% for testing in a caption, you can uncomment this part of code %%%
%\begin{figure}[h]
%\centering
%\includegraphics[scale=0.2]{images/Tux.png}
%\caption{A caption}%
%\end{figure}
%%% for testing in a floatrow, you can uncomment this part of code %%%
%\begin{figure}[ht]
% \centering
% {
% \begin{floatrow}[1]
% \ffigbox[\FBwidth]{\caption{A caption}}{\includegraphics[scale=0.3]{images/Tux.png}}
% \end{floatrow}
% }
%\end{figure}
\end{document}
答案1
您的测试\r@label
是否已定义标签根据.aux
文件。确实,\label
写入\newlabel
调用.aux
文件。该.aux
文件被读取:
在
\enddocument
时间上,这使得 LaTeX 能够对多重定义的标签发出警告;在开始文档时,允许文件
\newlabel
中存在的调用为上次编译运行期间定义的每个标签.aux
进行定义。\r@label
\label
因此:
当你的宏被
\r@test
定义时,这意味着它\label{test}
在上次编译运行;它将打印“已定义”和不会打电话\label{test}
在这次运行中,这适用于全部test
在此编译运行期间使用参数调用宏。下次编译时,
.aux
文件将不会有任何\newlabel
对标签的调用test
,因此您的宏将始终发现\r@test
未定义,并将始终\label{test}
在本次编译运行中调用,因此每次在本次编译运行中使用参数调用您的宏时,都会输出“标签‘测试’多次定义”警告test
。调用将对文件的调用写入\label{test}
文件,因此在下次编译运行中,我们将回到步骤 1。\newlabel
test
.aux
我相信你想要的是以下内容。\ifx\protect\@typeset@protect
可以让我们确保没有任何东西泄漏到图片标题中在表格列表或图片列表中(排版时测试正确,但通过 将标题写入.lot
或.lof
文件时测试不正确\addtocontents
——后者使用\protected@write
,这暂时使\protect
\let
-等于\@unexpandable@protect
)。
编辑:好吧,由于floatrow
工作方式的原因,处理\MyLabel
标题内部floatrow
要比这复杂得多,但下面的方法似乎可以正常工作。请注意,需要进行几次编译才能使标签稳定下来。
\documentclass{article}
\usepackage{etoolbox}
\usepackage{refcount}
\usepackage{graphicx} % only for testing
\usepackage{floatrow} % only for testing
\makeatletter
\newcommand*{\myInit}[1]{%
\renewcommand*{\do}[1]{\newcounter{mycount@##1}}%
\docsvlist{#1}%
\AtBeginDocument{%
\renewcommand*{\do}[1]{%
\ifcsundef{my@goodvalue@##1}{\def\@currentlabel{??}\label{##1}}{}}%
\docsvlist{#1}%
}%
}
\newcommand*{\my@MaybeDefine}[2]{%
\ifcsundef{my@goodvalue@#1}{\csgdef{my@goodvalue@#1}{#2}}{}%
}
\newcommand*{\my@WriteCtr}[2]{%
\write\@auxout{\string\my@MaybeDefine{#1}{#2}}%
}
\newcommand*{\MyLabel}[2]{%
\ifx\protect\@typeset@protect
\stepcounter{mycount@#1}%
\edef\my@internal@label{my@internal@label@#1@\number\value{mycount@#1}}%
\ifcsdef{my@goodvalue@#1}
{\ifnum\value{mycount@#1}=\csuse{my@goodvalue@#1}
\refstepcounter{#2}%
\label{#1}%
\else
\IfRefUndefinedBabel{#1}{}{% Ref #1 is defined
\IfRefUndefinedBabel{\my@internal@label}
{}
{%
\ifnum\getpagerefnumber{\my@internal@label}=\getpagerefnumber{#1}
the special label is defined earlier on the same page%
\else
\ifnum\getpagerefnumber
{\my@internal@label}>\getpagerefnumber{#1}
the special label was defined on an earlier page%
\fi
\fi
}%
}%
\fi
}
{\typeout{You need to rerun LaTeX for the special labels.}}%
\label{\my@internal@label}%
\begingroup
\edef\tmp{\endgroup\noexpand\my@WriteCtr{#1}{\number\value{mycount@#1}}}%
\tmp
\fi
}
\makeatother
\myInit{First-test, Second-test} % The special labels
\newcounter{example}
\setcounter{example}{0} % not really needed: this is done implicitly
\begin{document}
\listoffigures
\section{Introduction}
Try to label a first time\MyLabel{First-test}{example}.
Try to label a second time with the same: \MyLabel{First-test}{example}.
Label \verb|First-test| is on page~\pageref{First-test} and corresponds to
value~\ref{First-test} of the \verb|example| counter. Label \verb|Second-test|
is on page~\pageref{Second-test} and corresponds to value~\ref{Second-test} of
the \verb|example| counter.
\begin{figure}
\centering
\includegraphics[scale=0.2]{example-image-a}
\caption{A caption.}
\end{figure}
\begin{table}[p]
\centering
Some floating material that will appear late in the PDF output:
\MyLabel{Second-test}{example}.%
\label{a-table}%
\caption{A table environment}
\end{table}
\begin{figure}[ht]
\centering
\begin{floatrow}[1]
\ffigbox[\FBwidth]
{\caption{Another caption\MyLabel{Second-test}{example}}}
{\includegraphics[scale=0.3]{example-image-b}}
\end{floatrow}
\end{figure}
Calling \verb|\MyLabel{Second-test}{example}| a third time:
\MyLabel{Second-test}{example}.
\end{document}
怎么运行的
注意,这有点技术性。我们遇到的主要问题floatrow
是,它多次排版标题文本,\protect
等于\@typeset@protect
在我的测试中,单个标题排版 5 次!)。事实上,它似乎在决定发货之前以几种方式对其进行了测量。因此,对于每个特定的特殊标签(那些声明\myInit
和使用的\MyLabel
),我们需要检测它第一次被运出的地方(即发送到 DVI 或 PDF 文件)和仅限这次使用\label
。对于较早的时间,我们不能输出任何内容(否则,我们可能会干扰测量);对于较晚的时间,我们需要按照问题的要求输出“已定义”,但没有\label
调用。
现在,如何\MyLabel
检测给定标签首次发货的时间?对于每个标签,它会计算在排版模式下调用的次数(\protect
等于\@typeset@protect
),并将\write
计数器的相应值保存到.aux
文件中(这是value
中的\my@MaybeDefine{special label}{value}
)。这是主要技巧。A\write
是那是什么(参见 TeXbook),因此某些东西会进入盒子里,只有当装有东西的箱子运出后,才会真正写入文件floatrow
。因此,或其他用于测量标题文本和其他内容的包使用的虚拟调用以这种方式处理:不发送,不写入.aux
文件。value
第一次\my@MaybeDefine{special label}{value}
写入.aux
文件的表示第一次在发送的框中\MyLabel
使用第一个参数。因此,当 的内部计数器等于这个第一个值时,假设源文件自上次编译以来没有改变,这意味着包含的材料是第一次“真正”排版。special label
special label
special label
还有一件事:由于浮动(表格,数字......),一些与...相关的材料可能会特殊标签排版(即使\protect
等于\@typeset@protect
)早于\label
命令特殊标签,但稍后出现在输出文件中。在这种情况下,与特殊标签在为早期浮动排版材料时,其值会低于“合理值”,但由于材料将晚于出现,因此仍然需要“已定义”的文本\label
。因此,我添加了内部标签,当内部计数器与“合理值”不同时,我会将出现内部标签的页面(如果出现)与找到的页面进行比较\label{special label}
。当内容未发送出去(floatrow
进行测量等)时,相应的内部标签不会定义,因此我修改为“特殊标签在同一页面上较早定义”和“特殊标签在较早的页面上定义”的文本不会干扰测量(参见代码)。
是的,这有点儿太黑客了!