[Tex/LaTex] create a command that will conditionally gobble up trailing whitespace: I want an “\unxspace” command

macrosspacing

I have a command that occasionally produces whitespace that I don't want.

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

When \testa and \testb are different, I wind up getting unwanted whitespace in the following example:

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

I thought I could do something clever like

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

I thought this would make the test line above equivalent to the following

Testing \sweetnothing \sweetnothing \sweetnothing \sweetnothing Testing

But it's not.

I tried some \expandafter magic

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

Since I thought that would essentially unwind to \sweetnothing followed by a blank space.
No such luck.

Here's a 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}

I really wanted all three lines to look like line 4 when \testa and \testb are different.

Then I went off the deep end and tried something along the lines of

\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}

But this had completely unexpected results. I didn't really expect it to work, but I didn't expect to quite fail like this either.

enter image description here

What I thought I was doing was comparing the catcode of a space to the catcode passed of the first argument. If they were the same, then I wanted to discard the second argument (at that point there should be a recursive call—which I don't do). If they are not the same, then I want to keep the first argument.

Any ideas?

Best Answer

Let's see what happens: if \testa and \testb are equal, there is no problem; when \testa and \testb are different, from

Test \mycommand{this} is

you get "Test••is" (with • I denote a space in the output). This has nothing to do with the macro at hand, but it's a consequence of the rules. In fact, when \mycommand{this} is expanded to nothing, the input has already been tokenized, so TeX is not any more in the state where consecutive spaces are reduced to one space token.

How to remove the space depends on what you want to do. The difference between

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

and

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

is about what of the two spaces is removed; with \unskip it's the space before, with \ignorespaces it's the one after.

There is, however, a conceptual difference: \unskip just removes glue if it was there, and has nothing to do with macro expansion; \ignorespaces, instead, expands the following tokens until finding a non expandable one that is not a space token and gobbling all space tokens it finds in the process. (That's why it's not necessary to use \expandafter in front of it in the second definition, because it will start expanding the \fi).

Thus it may be preferable to use the first mode, if you're afraid that expansion can happen too early. Using \relax before \ifx, if you don't need \mycommand in an expansion only context, may prove useful.

Just to try being clearer, \unskip works down in the stomach, when commands are being executed; to the contrary, \ignorespaces works during macro expansion, much earlier than the stage when TeX executes commands. So the former can remove the last node, if it is glue, the latter cannot remove spacing instructions, but only space tokens.

Related Question