5

Please consider the following MWE - where I try to test for string equality between an parameter, and a test string prepared by a macro:

\documentclass{article}

\def\testStr#1{IOIO#1}

\newcommand{\doTest}[1]{%
\edef\tmphwp{\testStr{XX}} %
\typeout{=\tmphwp=#1=} %
\ifx\tmphwp#1{%
\typeout{~equal}%
}\else{%
\typeout{!not equal}%
}\fi %
\typeout{ ^^J } % newline in terminal stdout
}

\begin{document}

\doTest{12}

\doTest{hello}

\doTest{IOIOXX}

\end{document}

Strangely, this seems to always determine strings are unequal - even if I enter specifically the right string - terminal log (from pdflatex) reports:

=IOIOXX=12=
!not equal


=IOIOXX=hello=
!not equal


=IOIOXX=IOIOXX=
!not equal

Hmm ... I would have expected IOIOXX = IOIOXX ?!

Can anyone explain why am I not getting the expected results - and how to get the right comparison? Does this have maybe to do anything with catcodes?

Many thanks in advance for any answers,
Cheers!

1
  • You are doing \ifx\tmphwp IOIOXX which is of course false, because \tmphwp is a control sequence and I is a character.
    – egreg
    May 24, 2012 at 8:58

2 Answers 2

12

The main thing to note is that \ifx compares, without expanding them, the two tokens that follow. Now let's slowly look at what \dotest{IOIOXX} expands to.

  1. \edef\tmphwp{\testStr{XX}} that's equivalent to \def\tmphwp{IOIOXX}

  2. \typeout{=\tmphwp=#1=} that prints on the screen =IOIOXX=IOIOXX=

  3. The conditional

    \ifx\tmphwp IOIOXX{%
      \typeout{~equal}%
    }\else{%
      \typeout{!not equal}%
    }\fi %
    \typeout{ ^^J } % newline in terminal stdout
    

which is false, because \tmphwp is different from I.

You're making a few other errors: your way of writing the code leaves a fair amount of spurious spaces; the true and the false branches of the conditional are not required to be in braces, which in most cases would even be wrong.

What to do in order to compare two strings? Put the one given as argument in the replacement test of a temporary macro:

\def\dotest#1{%
  \def\next{#1}%
  \ifx\tmphwp\next
    \typeout{EQUAL}%
  \else
    \typeout{NOT EQUAL}%
  \fi
}

where you already have set somewhere \tmphwp to expand to the "control string" (I don't see why you should set it when doing the test itself), say with

\def\tmphwp{IOIOXX}

Let's say that \tmphwp expands to IOIOXX: now \ifx will compare the two control sequences \tmphwp and \next which are equal, because they are both not \long, their parameter text is the same (empty) and they have the same replacement text (IOIOXX).

Conversely, \dotest{AA} would evaluate the test to false.

1
  • Many thanks for that @egreg - I managed to independently arrive at a solution posted below; but I will accept your answer because of something I wasn't aware until now: "... \ifx compares, without expanding them, the two tokens ...". Many thanks again - cheers!
    – sdaau
    May 24, 2012 at 9:29
0

Ok, got this solved, thanks to tex core - Detecting catcode in parameter text - TeX - LaTeX; especially this note from @HendrikVogt:

This has nothing to do with catcodes of \fooA or \fooB. The \ifx test just sees that the parameter texts of the two macros are different (they contain different @s), so it turns out negative. The \ifx test doesn't care what the difference is.

... which means that, however, it does have to do with the catcodes of the characters of a "string content" of a "variable" (command)... and the \detokenize snippet by @Jason finally put this all in place.

One other problem I've frequently encountered, is that sometimes when I do, say \detokenize{\command} in an \edef and then try to \typeout the output - instead of the characters of the content, I see \command again (understood apparently as a single token). @Jason's use of \expandafter made me finally realize that such constructs should be written:

   \edef\tpmx{\expandafter\detokenize\expandafter{\mycommand}}

... the first \expandafter "escaping" the \detokenize - and the second one escaping the { curly brace - so that \mycommand can be expanded down to its content first; and \detokenize can subsequently work on the characters of the content (the variable value). (note however that there is no need for such escaping when \detokenize works on #1 - the stanza \edef\tmphwb{\detokenize{#1}} seems to work just fine)

So, in brief - it is about the catcodes (I guess); and once we have to compare strings that we expect should have the same (ASCII) content, but somehow ended up with differing catcodes (from say diverse macros) - then we can call \detokenize to "reset" the catcodes of characters of both strings before comparison - and then the \ifx comparison can be seen to work:

=IOIOXX=12=IOIOXX=12=
!not equal
!not equal
!not equal


=IOIOXX=hello=IOIOXX=hello=
!not equal
!not equal
!not equal


=IOIOXX=IOIOXX=IOIOXX=IOIOXX=
!not equal
!not equal
\~equal

The above pdflatex terminal output was produced with this modification of the OP MWE code:

\documentclass{article}

\def\testStr#1{IOIO#1}

\newcommand{\doTest}[1]{%
\edef\tmphwp{\testStr{XX}} %
\edef\tmphwb{\detokenize{#1}} %
% \edef\tmphwc{\detokenize{\tmphwp}} % NO: outputs \tmphwp!
% to have \detokenize{\tmphwp} show
%  actual content as expected here,
%  MUST use two \expandafter's:
%   one to skip \detokenize, and other to skip the
%   brace; - so \tmphwp can be expanded first!
\edef\tmphwc{\expandafter\detokenize\expandafter{\tmphwp}} %
\typeout{=\tmphwp=#1=\tmphwc=\tmphwb=} %
%
\ifx\tmphwp#1{%
  \typeout{\~equal}%
}\else{%
  \typeout{!not equal}%
}\fi %
%
\ifx\tmphwp\tmphwb{%
  \typeout{\~equal}%
}\else{%
  \typeout{!not equal}%
}\fi %
%
\ifx\tmphwc\tmphwb{%
  \typeout{\~equal}%
}\else{%
  \typeout{!not equal}%
}\fi %
\typeout{ ^^J } % newline in terminal stdout
}

\begin{document}

\doTest{12}

\doTest{hello}

\doTest{IOIOXX}

\end{document}

Well, hope this helps someone,
Cheers!

4
  • 1
    Category codes have nothing to do with the question as posed. Anyway, you're still making the same errors of considering the true and false branches as arguments in braces, which are not. You've been warned. :)
    – egreg
    May 24, 2012 at 9:39
  • Thanks for that, @egreg - I am aware I am missing a lot of details, and it is very hard for me to get rid of the OOP type of thinking; which is why I try to come up with examples that sort of emulate that behavior, and still work :) Hopefully that will eventually lead me to understand the details better eventually; since unfortunately right now I cannot afford to get through the Tex book and other resources in detail. Thus, warning appreciated (I'll sure get back to it at a certain point) :) Cheers!
    – sdaau
    May 24, 2012 at 9:44
  • 1
    \edef\x{\detokenize{\z}} and \edef\y{\detokenize\expandafter{\z}} will result in different macros if, say, \z expands to abc. There's no need for an \expandafter before \detokenize in the second case.
    – egreg
    May 24, 2012 at 9:44
  • Awesome, thanks for that example, @egreg - it points well why I should be careful with the code in above posting; it will take me some time to understand that a little better, though... Cheers!
    – sdaau
    May 24, 2012 at 9:47

You must log in to answer this question.

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