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.
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, fromyou 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
and
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.