如何在 \edef 中使用 \noexpand?

如何在 \edef 中使用 \noexpand?

继续努力解决扩展问题,我不知道如何\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}}

这里发生的事情是,定义xTeX 沿着替换文本中的每个标记扩展每个可扩展标记,直到将标记添加到定义时该标记不可扩展,然后扩展下一个标记,直到到达结束标记}

因此第一个标记是\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会完全按照您的意愿进行,没有任何麻烦。

相关内容