7

I don't understand why this code behaves as it does:

\newcommand{\f}{f}% food
\newcommand{\s}{}% stop eating
\newcommand{\h}[1]{h}% hide food
\newcommand{\e}[1]{% eat
  \ifx\f#1
    e
  \else
    #1
  \fi
}

% f represents food
% e represents eaten food
% [v] means it works as expected
% [x] means it doesn't

% [v] Eat food
[\e\f]% [e]

% [v] Stop eating
[\e\s\f]% [f]

% [v] Hide food
[\h\f]% [h]

% [x] Eat hidden food
[\e\h\f]% [hf]
% I expected [h]

I would've expected [\e\h\f][\h\f][h]. What did I miss?

Thank you in advance for your help.

1
  • You needed [\e{\h\f}] so \e captures \h\f and not just \h. Aug 5, 2015 at 15:25

2 Answers 2

7

If you add \show#1 to your definition of \h

 \newcommand{\h}[1]{\show#1h}% 

you will see that in your last case the argument of \h is not the \f but the \fi.

You should better move the output behind the \fi:

\documentclass{article}


\begin{document}
\makeatletter
\newcommand{\f}{f}% food
\newcommand{\s}{}% stop eating
\newcommand{\h}[1]{h}% hide food
\newcommand{\e}[1]{% eat
  \ifx\f#1\relax
   \expandafter\@firstoftwo 
  \else
   \expandafter\@secondoftwo 
  \fi
  {e}%
  {#1}%
}

% f represents food
% e represents eaten food
% [v] means it works as expected
% [x] means it doesn't

% [v] Eat food
[\e\f]% [e]

% [v] Stop eating
[\e\s\f]% [f]

% [v] Hide food
[\h\f]% [h]

% [x] Eat hidden food
[\e\h\f]% [hf]
\end{document}

enter image description here

3
  • Thank you for you very clear answer :) Just one questions: Why do you use \relax after the \ifx but comments after }?
    – xavierm02
    Aug 5, 2015 at 16:51
  • 1
    I used \relax after the \ifx to be sure that the test ends there -- a comment wouldn't stop it if the argument is empty. Aug 5, 2015 at 17:06
  • @UlrikeFischer Sorry, but I couldn't resist writing one of my overlong answers.
    – egreg
    Aug 5, 2015 at 17:42
3

The conditional \ifx compares the following two tokens and returns true if they are the same as far as \show is concerned. So two identical characters (by character code and category code), or two macros having the same status with respect to \long (and \protected if e-TeX is used) and the same first level expansion, or the same TeX primitive (one or both can be control sequences \let to the primitive). A control sequence \let to a character will be considered the same as the character.

In the case of \e\h\f, \e finds its argument to be \h, so TeX replaces the first two tokens and gets

\ifx\f\h•e•\else\h•\fi\f

(here denotes a space token). The test returns false, so TeX removes everything up to and including the matching \else. What remains at the next stage is

\h•\fi\f

and now \h finds its argument to be \fi (space tokens are ignored when looking for an undelimited argument), and the next stage is

h\f

Then TeX typesets h, expands \f that prints f and warns about an incomplete conditional, because there's no \fi any more:

\end occurred when \ifx on line 12 was incomplete

(the line number will of course be different).

On the contrary, \e\s\f will become

\ifx\f\s•e•\else\s•\fi\f

and then

\s•\fi\f

Since the expansion of \s is empty, you get

•\fi\f

and the space is typeset, the \fi is expanded (producing nothing, but keeping TeX happy about the count of conditional) and \f produces an ‘f’. Indeed, the output of [\e\s\f] is

[ f]

With \e\f the replacement gives

\ifx\f\f•e•\else\f•\fi

and the test returns true. In this case, TeX just removes the \ifx and the test tokens; the next stage is so

•e•\else\f•\fi

so “space e space” is printed; then \else removes everything up to the matching \fi. Indeed, the output of [\e\f] is

[ e ]

You should be more careful about spurious spaces and also pushing \else and \fi out of the way.

Ulrike's solution is good, and no space is added. However, if you want to have a fully expandable macro, do

\newcommand{\e}[1]{% eat
  \ifx\f#1%
   \expandafter\@firstoftwo 
  \else
   \expandafter\@secondoftwo 
  \fi
  {e}%
  {#1}%
}

but be sure to provide a non empty argument to \e. A different strategy is to compare the tokens with expl3:

\usepackage{xparse} % loads expl3

\ExplSyntaxOn
\DeclareExpandableDocumentCommand{\e}{m}
 {
  \token_if_eq_meaning:NNTF \f #1 { e } { #1 }
 }
\ExplSyntaxOff

Full code

\documentclass{article}
\usepackage{xparse}

\newcommand{\f}{f}% food
\newcommand{\s}{}% stop eating
\newcommand{\h}[1]{h}% hide food
\ExplSyntaxOn
\DeclareExpandableDocumentCommand{\e}{m}
 {
  \token_if_eq_meaning:NNTF \f #1 { e } { #1 }
 }
\ExplSyntaxOff
\begin{document}

% f represents food
% e represents eaten food

% Eat food
[\e\f]% [e]

% Stop eating
[\e\s\f]% [f]

% Hide food
[\h\f]% [h]

% Eat hidden food
[\e\h\f]% [h]

\end{document}

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .