有人可以解释一下导致此处出现空白的标记化过程吗?

有人可以解释一下导致此处出现空白的标记化过程吗?

这是一个产生虚假空白的 MWE:

\documentclass{article}
\usepackage[T1]{fontenc}
\usepackage{lmodern}
\newcommand\aetest[2][\relax]{%%
  \def\aejunk{<#2>}%%
  \ifx\relax#1
  \else
    \def\aejunk{(#2:#1)}%%
  \fi
  \fbox{\aejunk\rule[-0.5ex]{0pt}{2.5ex}}%%
  }

\begin{document}

  \aetest[world]{hello}%%
  \aetest{ciao}

\end{document}

在此处输入图片描述

我知道如何解决这个问题。我只需要\ifx\relax#1重写

\ifx\relax#1%%

但是我本以为,由于这个测试只有在 时才为真#1\relax所以后面的空格会被吸收,行尾注释应该是不必要的。我猜想我对标记化过程有些不理解。有人能解释一下这里发生了什么,导致空格悄悄出现吗?

答案1

当 TeX 吸收定义的替换文本时,它不会进行任何扩展(除非你使用\edef\xdef)。在你的替换文本中,你有

\ifx\relax#1

行尾算作空格,因为它不在控制字后面,因此在标记化过程中会被转换为空格。在使用宏时,不会在扩展过程中忽略空格,因为 TeX 不会查找<one optional space>,它会在数字常量之后或在赋值之后查找 ,=其中=是可选的,还有一些其他情况,甚至会忽略整个空格标记字符串。

#1在“无可选参数”的情况下,用 替换 的事实\relax完全无关紧要,因为只有在标记化过程中,控制字后的空格才会被忽略:空格代币控制字之后潜入的那些内容不会被忽略。

还要注意,在你进行的定义中,TeX 实际上执行了两个\def命令。首先它执行

\expandafter\def\expandafter\aetest\expandafter{%
  \expandafter\@protected@testopt
  \expandafter\aetest
  \csname\string\aetest\endcsname{\relax}%
}

进而

\expandafter\def\csname\string\aetest\endcsname[#1]#2{%
  \def\aejunk{<#2>}%%
  \ifx\relax#1
  \else
    \def\aejunk{(#2:#1)}%%
  \fi
  \fbox{\aejunk\rule[-0.5ex]{0pt}{2.5ex}}%%
  }

在第二个定义中,没有“无可选参数”的#1情况\relax。但是,正如我所说,这从一开始就无关紧要。

如果您想要对“无可选参数”进行安全测试,请使用xparse

\usepackage{xparse}

\NewDocumentCommand{\aetest}{om}{%
  \IfNoValueTF{#1}
    {\def\aejunk{<#2>}}
    {\def\aejunk{(#1:#2)}}%
   \fbox{\aejunk\rule[-0.5ex]{0pt}{2.5ex}}%
}

所以\aetest{ciao}\aetest[]{ciao}产生不同的结果。

另一方面,如果你只是想测试可选参数是否未提供或为空,通常

\if\relax\detokenize{#1}\relax

測試更好。

答案2

好的,我想我知道发生了什么。

当我定义\aetest

\newcommand\aetest[2][\relax]{%%
  \def\aejunk{<#2>}%%
  \ifx\relax#1
  \else
    \def\aejunk{(#2:#1)}%%
  \fi
  \fbox{\aejunk\rule[-0.5ex]{0pt}{2.5ex}}%%
  }

目前,还没有扩展#1#2尚未发生。因此,当\aetest定义时,行尾字符#1将被标记化。因为一旦空格被标记化,它就不会在以后消失:例如当

\aejunk{ciao}

得到扩大。

相关内容