在 LaTeX 文档中,我使用fancyref
软件包进行文档内引用。但是,我需要一堆自定义前缀来引用引理、定义和定理,而 fancyref 不提供这些。按照文档,添加一个支持两者的前缀,需要大量vario
文本,而且一直都一样。我想使用宏来避免这种情况,宏可以帮我完成所有工作,但我被它难住了。plain
fref
Fref
这是我目前的尝试:
\documentclass[a4paper]{article}
\usepackage{lmodern}
\usepackage[T1]{fontenc}
\usepackage[utf8]{inputenc}
\usepackage[english]{babel}
\usepackage[plain]{fancyref}
% command to define a fancyref prefix
\makeatletter
\def\mkfancyprefix#1#2{%
\@namedef{fancyref#1labelprefix}{#1}%
\frefformat{plain}{\@nameuse{fancyref#1labelprefix}}{%
\MakeLowercase{#2}\fancyrefdefaultspacing##1%
}%
\Frefformat{plain}{\@nameuse{fancyref#1labelprefix}}{%
#2\fancyrefdefaultspacing##1%
}%
}
\makeatother
% environments and references for lemmas, ...
\newtheorem{lemma}{Lemma}
\mkfancyprefix{lem}{Lemma}
% content
\begin{document}
\begin{lemma}
\label{lem:lemma}
This is a Lemma
\end{lemma}
There's \fref{lem:lemma} around! \Fref{lem:lemma} is the same, but at the beginning of a sentence.
\end{document}
但是,这无法编译,并抱怨:
Missing \begin{document}
如果我移动\mkfancyprefix{lem}{Lemma}
之后\begin{document}
,我可以看到它实际上输出了fancyreflemlabelprefixfancyreflemlabelprefix
。除此之外,一切都正常并且看起来符合预期。
这让我很困惑,为什么命令会将 的扩展参数打印\@nameuse
到文档中?我做了一些实验,并\expandafter
在 两次出现 之前立即添加了\@nameuse
。这会将输出更改为lemlem
,但这并没有减少我的困惑。
为什么会这样?我该如何防止?
附录:
Enrico 提供 对我下面的问题有很好的回答。稍微扩展一下,就会得到一个命令,它fancyref
在格式变体vario
和中声明一个新的前缀plain
。如果你使用其他的(比如main
),你必须相应地扩展宏。
\makeatletter
\def\mkfancyprefix#1#2{%
\expandafter\def\csname fancyref#1labelprefix\endcsname{#1}%
% plain lowercase
\begingroup\def\x{\endgroup\frefformat{plain}}%
\expandafter\x\csname fancyref#1labelprefix\endcsname
{\MakeLowercase{#2}\fancyrefdefaultspacing##1}%
% plain uppercase
\begingroup\def\x{\endgroup\Frefformat{plain}}%
\expandafter\x\csname fancyref#1labelprefix\endcsname
{#2\fancyrefdefaultspacing##1}%
% vario lowercase
\begingroup\def\x{\endgroup\frefformat{vario}}%
\expandafter\x\csname fancyref#1labelprefix\endcsname
{\MakeLowercase{#2}\fancyrefdefaultspacing##1##3}%
% vario uppercase
\begingroup\def\x{\endgroup\Frefformat{vario}}%
\expandafter\x\csname fancyref#1labelprefix\endcsname
{#2\fancyrefdefaultspacing##1##3}%
}
\makeatother
答案1
问题是,\frefformat
期望它的第二个参数是一个控制序列,而不是生成控制序列的指令;所以你必须在 TeX 看到之前构建标记\frefformat
:
\makeatletter
\def\mkfancyprefix#1#2{%
\@namedef{fancyref#1labelprefix}{#1}%
\begingroup\def\x{\endgroup\frefformat{plain}}%
\expandafter\x\csname fancyref#1labelprefix\endcsname
{\MakeLowercase{#2}\fancyrefdefaultspacing##1}%
\begingroup\def\x{\endgroup\Frefformat{plain}}%
\expandafter\x\csname fancyref#1labelprefix\endcsname
{#2\fancyrefdefaultspacing##1}%
}
\makeatother
诀窍\x
就是能够使用\expandafter
。
它是如何工作的?当\mkfancyprefix{lem}{Lemma}
被调用时,TeX 会
\@namedef{fancyreflemlabelprefix}{lem}
相当于\def\fancyreflemlabelprefix{lem}
。然后它会找到
\begingroup\def\x{\endgroup\frefformat{plain}}%
\expandafter\x\csname fancyreflemlabelprefix\endcsname
{\MakeLowercase{Lemma}\fancyrefdefaultspacing#1}
(现在##1
定义中的变成了应该的样子)。执行 之后,LaTeX 仍然有一个待处理项,并发现,因此它会扩展(一次),从而产生标记。接下来会进行扩展,因此 TeX 面临\mkfancyref
#1
\def\x{...}
\begingroup
\expandafter\x
\csname fancyreflemlabelprefix\endcsname
\fancyreflemlabelprefix
\x
\endgroup\frefformat{plain}\fancyreflemlabelprefix
{\MakeLowercase{Lemma}\fancyrefdefaultspacing#1}
匹配\endgroup
仍然待处理的\begingroup
并从内存中删除的定义\x
;然后正确的\frefformat
指令就可以被处理了。