继续努力解决扩展问题,我不知道如何\edef
正确使用。下面的代码中的开关设置为:
%\def\ApplyColorToTitle{}
%\def\UseMathrmInTitle{}
\def\UseEdefInsteasOfDef{}
生成:
但是,当我尝试应用标题的颜色格式,甚至在标题中使用\mathrm{}
(令人惊讶的是,使用不会出现此问题)时,如果我尝试使用,就会出现编译错误。也就是说,以下设置有效:\text{}
\edef
\def\ApplyColorToTitle{}
\def\UseMathrmInTitle{}
%\def\UseEdefInsteasOfDef{}
并产生所需的结果:
不过,我希望能够在启用所有开关的情况下使用此代码:
\def\ApplyColorToTitle{}
\def\UseMathrmInTitle{}
\def\UseEdefInsteasOfDef{}
在这种情况下,我想法\noexpand
我需要在格式化宏中使用 \edef
:
\edef\FormattedTitle{\noexpand\FormatTitle{\Title}}
这\noexpand
对于以下情况确实有效:
\def\ApplyColorToTitle{}
%\def\UseMathrmInTitle{}
\def\UseEdefInsteasOfDef{}
因此我认为我走在正确的轨道上,但使用仍然失败\mathrm{}
。
问题:
我如何使用\edef
而不是\def
和\mathrm{}
允许与颜色格式一起使用吗?
笔记:
我必须承认,我不确定为什么我
\edef
首先在这里使用 ,但肯定有一个案例无法正常工作,当我切换到使用 时,该案例得到了修复\edef
。我意识到这不是一个很好的理由,但这就是我宁愿继续使用 的原因\edef
。如果这里的扩展专家认为没有理由\edef
在这里使用 并建议我只使用 ,\def
我会这样做,直到我能够提供真正需要 的 MWE\edef
。我确实需要使用
\mathrm{}
而不是\text{}
来确保th
采用罗马字体,即使标题被包裹在另一个格式化宏中。
代码:
%\def\ApplyColorToTitle{}
%\def\UseMathrmInTitle{}
\def\UseEdefInsteasOfDef{}
\documentclass{article}
\usepackage{xcolor}
\usepackage{amsmath}
\newcommand*{\Title}{}%
\newcommand*{\SetTitle}[1]{%
\renewcommand*{\Title}{#1}%
}%
\ifdefined\ApplyColorToTitle
\newcommand{\FormatTitle}[1]{\textbf{\textcolor{blue}{\boldmath#1}}}%
\else
\newcommand{\FormatTitle}[1]{#1}%
\fi
\begin{document}
\ifdefined\UseMathrmInTitle
\SetTitle{$n^{\mathrm{th}}$ Root}%
\else
\SetTitle{$n^{\text{th}}$ Root}%
\fi
\ifdefined\UseEdefInsteasOfDef% Would prefer to use an edef
\edef\FormattedTitle{\FormatTitle{\Title}}%
% Thought that adding this \noexpand would do the trick as
% it seems to alow for \ApplyColorToTitle being defined.
%\edef\FormattedTitle{\noexpand\FormatTitle{\Title}}%
\else
\def\FormattedTitle{\FormatTitle{\Title}}%
\fi
Title is: \FormattedTitle
\end{document}
答案1
\edef\FormattedTitle{\noexpand\FormatTitle{\noexpand\Title}}%
有效,但这与\edef
根本不使用是一样的。
基本上答案确实是不要这样做.\noexpand
就停止了一扩张正在发生,所以你需要确切地正确的 s 数量\noexpand
取决于 token 经过的扩展上下文数量。太多的话 token 最终会表现得\relax
什么都不做。太少的话事情就会崩溃。
LaTeX\protect
机制(或 e-tex 保护原语)旨在避免这种情况,它会像 via 一样保留标记,\noexpand
直到您想要执行它们。但这(对于经典的 LaTeX 保护)意味着使用\protected@edef
。即使使用\protected@edef
,您也必须小心不要抑制命令的扩展,而要允许其参数扩展。如果你这样做
\noexpand\FormatTitle{\Title}
然后\FormatTitle
扩展到自身但\Title
仍正常扩展。
您收到的错误消息
\edef\x{\mathrm}
相当模糊
! Undefined control sequence.
\GenericError ...
#4 \errhelp \@err@ ...
l.6 \edef\x{\mathrm
}
了解原因和它们...
是什么是非常有趣的(对于某些娱乐的定义而言)。
让我们从一个更简单的情况开始。
如果你试试
\edef\x{\def\something{foo}}
你收到错误
! Undefined control sequence.
l.7 \edef\x{\def\something
{foo}}
这里发生的事情是,定义x
TeX 沿着替换文本中的每个标记扩展每个可扩展标记,直到将标记添加到定义时该标记不可扩展,然后扩展下一个标记,直到到达结束标记}
。
因此第一个标记是\def
。这是不可扩展的,因此只需将其添加到 的定义中\x
。下一个标记\something
是故意的是定义的标记,\def
但该定义尚未执行,因此 edef 为真以进行扩展\something
,并且您会收到未定义的控制序列错误(如果未定义该命令)。
如果你第一次去
\let\something\relax
因此\something
不可扩展,那么初始 edef 不会产生错误并产生
> \x=macro:
->\def \something {foo}.
l.10 \show\x
因此,在扩展的过程中,\mathrm
有一个测试和一个错误消息,以防测试失败,但在 edef 中,测试实际上并没有发生。TeX 只是扩展了所有的标记,所以它最终会扩展,\GenericError
它具有刚刚描述的结构,但有一个稍微不寻常的控制序列,其名称为
\@err@ %
也就是说,它的名字包含 65 个空格字符!
事实证明这是唯一未定义的标记,因此如果您定义此标记,则不会收到错误:
\expandafter\let\csname @err@\space
\space\space\space\space\space\space\space\space\space\space\space\space
\space\space\space\space\space\space\space\space\space\space\space\space
\space\space\space\space\space\space\space\space\space\space\space\space
\space\space\space\space\space\space\space\space\space\space\space\space
\space\space\space\space\space\space\space\space\space\space\space\space
\space\space\space\space\endcsname
\relax
\edef\x{\mathrm}
\show\x
生成:
> \x=macro:
->\protect \relax \protect \begingroup \immediate \write \@unused \def \Messa
geBreak
\let \protect \edef\@err@
You're in trouble here. Try typing <return> to proceed.\Messa
geBreak If that doesn't work, type X <return> to quit. \errhelp \@err@
\let \@err@
\def \MessageBreak
\def \errmessage LaTeX Error: \mathrm allowed only in math m
ode.
See the LaTeX manual or LaTeX Companion for explanation.
Type H <return> for immediate help\@err@
\endgroup \relax .
l.18 \show\x
但请注意,虽然模糊的错误已经消失,但定义的命令\x
完全无用。例如,它包含
\let \protect \edef
这是因为原版有一个扩展,如
\let \protect \string
\edef....
但\edef
已留下\let \protect
但\string
已扩展将\edef
标记转换为 5 个字符的标记\ e d e f
因此如果执行\let
将定义\protect
为反斜杠字符标记。
答案2
让我们看看当你的所有三个定义都设置好后会发生什么:
\newcommand*{\Title}{}
\newcommand*{\SetTitle}[1]{\renewcommand*{\Title}{#1}}
\newcommand{\FormatTitle}[1]{\textbf{\textcolor{blue}{\boldmath#1}}}
\SetTitle{$n^{\mathrm{th}}$ Root}
\edef\FormattedTitle{\FormatTitle{\Title}}
\edef
导致第一个的扩展\FormatTitle
,所以在第一阶段我们得到
\textbf{\textcolor{blue}{\boldmath\Title}}
现在\textbf
展开,得到\protect\textbf•
(项目符号表示宏名称中的空格)。此时\protect
相当于\relax
放在一边,不可展开,展开继续\textbf•
,得到
\ifmmode\nfss@text{\bfseries#1}\else\hmode@bgroup\text@command{#1}\bfseries\check@icl#1\check@icr\expandafter\egroup\fi
(其中#1
实际上是\textcolor{blue}{\boldmath\Title}
)。我们不在数学模式中,所以下一个要扩展的标记是\text@command
,它以 开头\def\reserved@a
:哦,天哪!,下一个要扩展的标记是 ,\reserved@a
它具有完全不可预测的值。它是 LaTeX 内核中经常使用的临时控制序列,它错误的尝试在这里扩展它,因为我们真的想赋予它一个新的含义,而这在 期间是不可能的\edef
。
让我们改变并使用\protected@edef
\newcommand*{\Title}{}
\newcommand*{\SetTitle}[1]{\renewcommand*{\Title}{#1}}
\newcommand{\FormatTitle}[1]{\textbf{\textcolor{blue}{\boldmath#1}}}
\SetTitle{$n^{\mathrm{th}}$ Root}
\makeatletter
\protected@edef\FormattedTitle{\FormatTitle{\Title}}
\makeatother
我不想执行所有步骤;但\protected@edef
会改变的含义,\protect
以便扩展\FormattedTitle
为
\protect\textbf•{\protect\leavevmode{\protect\color•{blue}\relax\protect\mathversion•{bold}$n^{\protect\mathrm•{th}}$ Root}}
(再次,项目符号表示控制序列名称中的空格)。
很容易看出,\protected@edef
在这种情况下,没有任何意义。事实上,如果使用简单\def
,代码将是
\newcommand*{\Title}{}
\newcommand*{\SetTitle}[1]{\renewcommand*{\Title}{#1}}
\newcommand{\FormatTitle}[1]{\textbf{\textcolor{blue}{\boldmath#1}}}
\SetTitle{$n^{\mathrm{th}}$ Root}
\def\FormattedTitle{\FormatTitle{\Title}}
并且呼叫\FormattedTitle
会完全按照您的意愿进行,没有任何麻烦。