我想创建一个有条件地吞噬尾随空格的命令:我想要一个“\unxspace”命令

我想创建一个有条件地吞噬尾随空格的命令:我想要一个“\unxspace”命令

我有一个命令偶尔会产生我不想要的空格。

\def\testa{a}
\def\testb{a}
\newcommand{\mycommand}[1]{\ifx\testa\testb{#1}\fi}

\testa\testb不同时,我最终会在以下示例中得到不需要的空格:

    Testing \mycommand{This} \mycommand{is} \mycommand{my} \mycommand{trial} \mycommand{run.} Testing

我以为我可以做一些聪明的事情

\def\sweetnothing{}
\def\testa{a}
\def\testb{b}
\newcommand{\mycommand}[1]{\ifx\testa\testb{#1}\else\sweetnothing\fi}

我认为这会使上面的测试行等同于以下内容

Testing \sweetnothing \sweetnothing \sweetnothing \sweetnothing Testing

但事实并非如此。

我尝试了一些\expandafter魔法

\newcommand{\mycommand}[1]{\ifx\testa\testb{#1}\else\expandafter\sweetnothing\fi}

因为我认为那基本上会展开到\sweetnothing后面有一个空白处。但没那么幸运。

这是一个 M(non)WE:

\documentclass{article}
\makeatletter
%%
\def\testa{a}
\def\testb{b}
\newcommand{\mycommand}[1]{\ifx\testa\testb{#1}\fi}
%%
\def\sweetnothing{}
\newcommand{\mycommandvar}[1]{\ifx\testa\testb{#1}\else\sweetnothing\fi}
%%
\makeatother
\pagestyle{empty}
\begin{document}
\textbf{Line 1:} Testing \mycommand{This} \mycommand{is} \mycommand{my} \mycommand{trial} \mycommand{run.} Testing

\textbf{Line 2:} Testing \mycommandvar{This} \mycommandvar{is} \mycommandvar{my} \mycommandvar{trial} \mycommandvar{run.} Testing

\textbf{Line 3:} Testing {} {} {} {} {} Testing

\textbf{Line 4:} Testing \sweetnothing \sweetnothing \sweetnothing \sweetnothing Testing
\end{document}

我真的希望所有三行看起来都像第 4 行,但实际上\testa它们\testb是不同的。

然后我又开始胡思乱想,尝试了类似这样的东西

\documentclass{article}
\makeatletter
\def\testa{a}
\def\testb{b}
\def\eat@white@space#1{\ifcat #1\relax\else#1\fi}
\newcommand{\mycommand}[2]{\ifx\testa\testb{#1#2}\else\expandafter\eat@white@space\fi}
\makeatother
\pagestyle{empty}
\begin{document}
Testing \mycommand{This} \mycommand{is} \mycommand{my} \mycommand{trial} \mycommand{run.} Testing
\end{document}

但结果完全出乎我的意料。我没想到它会成功,但也没料到会失败成这样。

在此处输入图片描述

想法我所做的是将空间的 catcode 与第一个参数传递的 catcode 进行比较。如果它们相同,那么我想丢弃第二个参数(此时应该有一个递归调用---我不会这样做)。如果它们不相同,那么我想保留第一个参数。

有任何想法吗?

答案1

让我们看看会发生什么:如果\testa\testb相等,就没有问题;当\testa\testb不同时,

Test \mycommand{this} is

您将得到“Test••is”(其中 • I 表示输出中的空格)。这与手头的宏无关,但它是规则的结果。事实上,当\mycommand{this}扩展为零时,输入已经被标记,因此 TeX 不再处于连续空格减少为一个空格标记的状态。

如何删除空格取决于你想做什么。

\newcommand{\mycommand}[1]{%
  \ifx\testa\testb
    #1%
  \else
    \ifhmode\unskip\fi
  \fi}

\newcommand{\mycommand}[1]{%
  \ifx\testa\testb
    #1%
  \else
    \ignorespaces
  \fi}

是关于删除两个空格中的哪一个;用\unskipit 表示删除之前的空格,用\ignorespacesit 表示删除之后的空格。

但是,存在一个概念上的区别:\unskip如果存在粘连,则仅将其移除,与宏扩展无关;\ignorespaces相反,它会扩展以下标记,直到找到一个不是空间标记的不可扩展标记,并吞噬它在此过程中找到的所有空间标记。 (这就是为什么在第二个定义中没有必要\expandafter在它前面使用,因为它将开始扩展\fi)。

因此,如果您担心扩展可能过早发生,最好使用第一种模式。如果您不需要在仅扩展的环境中使用,则使用\relaxbefore可能会很有用。\ifx\mycommand

为了更清楚起见,\unskip在执行命令时,它在胃中起作用;相反,\ignorespaces在宏扩展期间起作用,比 TeX 执行命令的阶段早得多。因此,如果最后一个节点是胶合节点,前者可以删除最后一个节点,而后者不能删除间距指令,只能删除空格标记。

答案2

@egreg 已经回答了主要问题,所以我想我应该解释一下

\documentclass{article}
\makeatletter
\def\testa{a}
\def\testb{b}
\def\eat@white@space#1{\ifcat #1\relax\else#1\fi}
\newcommand{\mycommand}[2]{\ifx\testa\testb{#1#2}\else\expandafter\eat@white@space\fi}
\makeatother
\pagestyle{empty}
\begin{document}
Testing \mycommand{This} \mycommand{is} \mycommand{my} \mycommand{trial} \mycommand{run.} Testing
\end{document}

\ifcat扩展标记,直到找到两个不可扩展的标记,然后比较它们的类别代码(为此,任何非字符标记都假定为 16 的假类别代码)。这两个不可扩展的标记将在测试中使用。如果测试为真,则处理从下一个标记开始,并\else定义为跳转到匹配的\fi。如果测试为假,TeX 将跳转到匹配的\else\fi并重新开始处理。

所以鉴于\mycommand{This} \mycommand{is}

这两个参数,#1#2\mycommandThis\mycommand

测试\ifx结果为假,因此处理跳至,\else输入流如下所示

 \expandafter\eat@white@space\fi{is}

\expandafter扩展了\fi所以我们有

 \eat@white@space{is}

这样就扩展为

\ifcat is\relax\else is\fi

i和的 catcodes匹配,因此扩展为\relax,并且输入流现在看起来像

\mycommand{my} \mycommand{trial} \mycommand{run.} Testing

处理过程和以前一样,只是这次我们要

 \eat@white@space{trail}

扩展为

\ifcat trial\relax\else is\fi

这次比较t和的 catcode r,因此它扩展为

ial\relax

ial最终按照您展示的方式进行排版。

答案3

你要找的宏是\unskip。如果你将以下两行

\newcommand{\mycommand}[1]{\ifx\testa\testb{#1}\else\unskip\fi}
\newcommand{\mycommandvar}[1]{\ifx\testa\testb{#1}\else\sweetnothing\unskip\fi}

你得到了想要的结果:

在此处输入图片描述

代码:

\documentclass{article}
\makeatletter
%%
\def\testa{a}
\def\testb{b}
\newcommand{\mycommand}[1]{\ifx\testa\testb{#1}\else\unskip\fi}
\def\sweetnothing{}
\newcommand{\mycommandvar}[1]{\ifx\testa\testb{#1}\else\sweetnothing\unskip\fi}
%%
\makeatother
\pagestyle{empty}
\begin{document}
\textbf{Line 1:} Testing \mycommand{This} \mycommand{is} \mycommand{my} \mycommand{trial} \mycommand{run.} Testing

\textbf{Line 2:} Testing \mycommandvar{This} \mycommandvar{is} \mycommandvar{my} \mycommandvar{trial} \mycommandvar{run.} Testing

\textbf{Line 3:} Testing {} {} {} {} {} Testing

\textbf{Line 4:} Testing \sweetnothing \sweetnothing \sweetnothing \sweetnothing Testing
\end{document}

相关内容