在回答实际问题之前,让我先介绍一下背景知识。我查看了一下latex.ltx
标签的处理方式。我发现:
\def\label#1{\@bsphack
\protected@write\@auxout{}%
{\string\newlabel{#1}{{\@currentlabel}{\thepage}}}%
\@esphack}
定义\label
为本质上\newlabel
向.aux
文件写入命令。让我来解释一下。\protected@write
如下:
\long\def \protected@write#1#2#3{%
\begingroup
\let\thepage\relax
#2%
\let\protect\@unexpandable@protect
\edef\reserved@a{\write#1{#3}}%
\reserved@a
\endgroup
\if@nobreak\ifvmode\nobreak\fi\fi
}
这是一篇关于 的有趣读物\let\thepage\relax
,可能也会证明分组的合理性。#2
中为空,\label
所以谁在乎呢。 的名称\@unexpandable@protect
是不言自明的,正如\def\@unexpandable@protect{\noexpand\protect\noexpand}
所证实的那样。 接下来是\@auxout
,它是当前文件,即如果我们处于包含文件的中间,.aux
则为\@mainaux
或。 因此,我们了解了 的所有内容,除了和。\@partaux
\label
\@bsphack
\@esphack
这和这解释一下。然后我发现在.aux
我的一个文件中,我确实找到了:
\newlabel{thm:defi:SM}{{1.2.1.1}{2}{Spazio Metrico}{defi.1.2.1.1}{}}
\label
我想知道为什么第二个括号对中有 5 个参数,而定义中只写两个参数,以及它们是如何使用的。所以我尝试阅读\ref
:
\def\ref#1{\expandafter\@setref\csname r@#1\endcsname\@firstoftwo{#1}}
顺便说一句,我发现与 保存\pageref
相同的是而不是。是:\ref
\@secondoftwo
\@firstoftwo
\@setref
\def\@setref#1#2#3{%
\ifx#1\relax
\protect\G@refundefinedtrue
\nfss@text{\reset@font\bfseries ??}%
\@latex@warning{Reference `#3' on page \thepage \space
undefined}%
\else
\expandafter#2#1\null
\fi}
如果标签未定义,则\csname r@#1\endcsname
传递给\@setref
的\ref
为\relax
,因此在这种情况下,我们会输出粗体??(带有一些我没有费心去解读的字体命令)和警告,否则我们会得到#2#1
。#2
是\@firstoftwo
或\@secondoftwo
取决于是否\@setref
由\ref
或调用\pageref
。#3
是标签,并且仅在警告消息中使用。什么定义了\csname r@#1\endcsname
?因为的其余部分\@setref
是\expandafter#2#1\null
,并且\null
只是一个空的\hbox
,可能是为了避免不必要的交互而与后面的内容分开。所以我们进入\newlabel
:
\def\newlabel{\@newl@bel r}
\@onlypreamble\@newl@bel
\def\@newl@bel#1#2#3{{%
\@ifundefined{#1@#2}%
\relax
{\gdef \@multiplelabels {%
\@latex@warning@no@line{There were multiply-defined labels}}%
\@latex@warning@no@line{Label `#2' multiply defined}}%
\global\@namedef{#1@#2}{#3}}}
假设警告被正确处理,就像 一样\@ifundefined
,如果 已经定义,这应该发出警告r@#1
,如果未定义,则不执行任何操作,无论如何都会这样做\@namedef
,即:
\def\@namedef#1{\expandafter\def\csname #1\endcsname}
这是的定义\csname r@<label>\endcsname
:它是的#3
,它是#2
的\newlabel
(或者更确切地说,是什么似乎在用户级别(或者更确切地说是用户级别)是。因此,这就是 中#2
的内容。一开始是假的,然后在文档处理过程中被修改,因此跟踪其变化基本上是不可能的。然而,问题仍然存在:如何获取伪 中的 5 个括号对,为什么如果我们只有and而不是or 之类的东西,我在输出中看不到它们?答案在于,它是:\newlabel
.aux
{{\@currentlabel}{\thepage}}
\csname r@<label>\endcsname
\@currentlabel
#2
\newlabel
\@firstoftwo
\@secondoftwo
\@firstoffive
hyperref
\let
\ref
\def\HyPsd@ref#1{\HyPsd@@ref#1*\END}%
\def\HyPsd@@ref#1*#2\END{%
\ifx\\#2\\%
\HyPsd@@@ref{#1}%
\else
\expandafter\HyPsd@@@ref
\fi
}%
\def\HyPsd@@@ref#1{%
\expandafter\ifx\csname r@#1\endcsname\relax
??%
\else
\expandafter\expandafter\expandafter
\@car\csname r@#1\endcsname\@nil
\fi
}
顺便说一句,它对 做了类似的事情\pageref
。#2
of\HyPsd@@ref
作为空参数传递,在条件中给出 true 并使 LaTeX 执行\HyPsd@@@ref{#1}
,其中的#1
参数是to。顺便说一句,每当发生上述情况时,就是to ,那么 是从哪里来的?也许吧?好吧,我们稍后再讲。假设 cs 的定义与之前相同,除了它具有上述方式中看到的所有 5 个括号对,我们看到如果 cs 未定义,则输出 ??,否则 cs 将由 s 生成并扩展为适当的数字,然后传递给\HyPsd@ref
\ref
\let
\label
\let
\@gobble
\let
\csname r@<label>\endcsname
thmtools
\newlabel
\expandafter
@car…\@nil
,它的作用是\@firstoffive
。替代方法\pageref
可能使用\@cbr
,它与 相同,只是\@car
它需要第二个参数。所以现在我知道了如何在 pdf 字符串中处理参数,因为我看到\let
上面描述的 s 发生在 中\pdfstringdef
。定理是使用 pdf 字符串的示例,也是我真正感兴趣的东西,可能一直以来都是如此。我使用thmtools
。所以现在的问题是:密钥label=
到底有什么用?
总结一下,问题是:
label=
包中的 key到底起什么作用thmtools
?它\label
在内部使用吗?- 那 5 个括号对是什么?它们似乎是
{ctr sequence associated with the theorem}{page on which the theorem starts}{theorem name= key}{theorem ctr name.ctr sequence}{empty}
。这是通用的吗?空括号对有什么用? - 额外的 3 个“参数”如何使用?
更新:
再看另一个.aux
,我注意到方程式的标签有类似的东西。我在顶部也看到了这个:
\providecommand\HyperFirstAtBeginDocument{\AtBeginDocument}
\HyperFirstAtBeginDocument{\ifx\hyper@anchor\@undefined
\global\let\oldcontentsline\contentsline
\gdef\contentsline#1#2#3#4{\oldcontentsline{#1}{#2}{#3}}
\global\let\oldnewlabel\newlabel
\gdef\newlabel#1#2{\newlabelxx{#1}#2}
\gdef\newlabelxx#1#2#3#4#5#6{\oldnewlabel{#1}{{#2}{#3}}}
因此,在文档的开头,\newlabel
本身被重新定义,使其吞噬了额外的“参数”。这使得问题变得更加紧迫:这些参数是用来做什么的?这也引出了一个新问题:为什么\newlabel
重新定义\let
为旧含义\AtEndDocument
?在什么条件下会发生重新定义?那是什么\hyper@anchor
?但也许我问的问题太多了。
答案1
让我们检查一份简单的文档。
\documentclass{article}
\usepackage{amsthm,thmtools}
\usepackage{hyperref}
\declaretheorem{theorem}
\begin{document}
\section{A section title}\label{sec-a}
\begin{theorem}[label=thm-e]
$0\ne1$
\end{theorem}
\begin{theorem}[name=Key theorem,label=thm-key]
$0=0$
\end{theorem}
\end{document}
\newlabel
我们在文件中找到的条目.aux
是
\newlabel{sec-a}{{1}{1}{A section title}{section.1}{}}
\newlabel{thm-e}{{1}{1}{}{theorem.1}{}}
\newlabel{thm-key}{{2}{1}{Key theorem}{theorem.2}{}}
如果hyperref
不使用,条目将会不同:
\newlabel{sec-a}{{1}{1}}
\newlabel{thm-e}{{1}{1}}
\newlabel{thm-key}{{2}{1}}
因此我们看到,这hyperref
扩展了文件中写出的数据.aux
。第二个参数\newlabel
包含五个括号对:
- 计数器的值
- 页码
- 标题
- 超链接锚点
\ref
- 空(为将来的扩展保留)
标题字段用于\nameref
;当您name=...
在 的可选参数中使用 时\begin{theorem}
,您也可以使用\nameref{thm-key}
,并且名称将被打印,就像\nameref{sec-a}
。
请注意,这thmtools
没有什么特别的。输入
\documentclass{article}
\usepackage{amsthm}
\usepackage{hyperref}
\newtheorem{theorem}{Theorem}
\begin{document}
\section{A section title}\label{sec-a}
\begin{theorem}\label{thm-e}
$0\ne1$
\end{theorem}
\begin{theorem}[Key theorem]\label{thm-key}
$0=0$
\end{theorem}
\end{document}
文件中的注释.aux
将是相同的:
\newlabel{sec-a}{{1}{1}{A section title}{section.1}{}}
\newlabel{thm-e}{{1}{1}{}{theorem.1}{}}
\newlabel{thm-key}{{2}{1}{Key theorem}{theorem.2}{}}
label=thm-e
可选参数中的键在处理过程中被简单地翻译\label{thm-e}
成\begin{theorem}
。
代码
\providecommand\hyper@newdestlabel[2]{}
\providecommand\HyperFirstAtBeginDocument{\AtBeginDocument}
\HyperFirstAtBeginDocument{\ifx\hyper@anchor\@undefined
\global\let\oldcontentsline\contentsline
\gdef\contentsline#1#2#3#4{\oldcontentsline{#1}{#2}{#3}}
\global\let\oldnewlabel\newlabel
\gdef\newlabel#1#2{\newlabelxx{#1}#2}
\gdef\newlabelxx#1#2#3#4#5#6{\oldnewlabel{#1}{{#2}{#3}}}
\AtEndDocument{\ifx\hyper@anchor\@undefined
\let\contentsline\oldcontentsline
\let\newlabel\oldnewlabel
\fi}
\fi}
.aux
是针对使用 loaded 写入文件hyperref
,然后在不使用 的情况下重新处理文件的情况的一种保护措施hyperref
。.aux
在这种情况下,当在 begin document 处读取文件时,额外的参数会使 LaTeX 感到困惑,因此命令\newlabel
被临时重新定义为执行它通常在不使用 时执行的操作hyperref
,并且额外的参数会被吞掉。
阅读完.aux
开始文档的文件后,LaTeX 重新打开该文件进行写入,从而生成一个新版本。
类似的治疗方法也适用于\contentsline
。