7
\documentclass{article}
\usepackage{xifthen, xstring}

\newcommand{\IfOne}[3]{
   \if\TokenIsIn#1
     #2
   \else
     #3
   \fi
}

\newcommand{\IfTwo}[3]{
\ifthenelse{\equal{\TokenIsIn}{#1}}{#2}{#3}
}

\newcommand{\IfThree}[3]{
\IfBeginWith{\TokenIsIn}{#1}{#2}{#3}
}
\newcommand{\TokenIsIn}{}
\newcommand{\settok}[1]{\global\let\TokenIsIn=#1}

\settok{a}

\begin{document}
\IfOne{a}{Yes}{No}\\
\IfTwo{a}{Yes}{No}\\
\IfThree{a}{Yes}{No}
\end{document}

Output

Yes
No
No

Why do the \IfTwo and \IfThree return No?

0

3 Answers 3

5

With \let\TokenIsIn=a, the control sequence \TokenIsIn becomes an unexpandable token; more precisely, it becomes an “implicit a”.

The tests \if\TokenIsIn a and \ifx\TokenIsIn a return true, but \ifthenelse makes the comparison in a different way; basically, \ifthenelse{\equal{X}{Y}} does something like

\edef\first{X}\edef\second{Y}\ifx\first\second

and this test returns false when X is \TokenIsIn and Y is a, because \first and \second don't have the same first level expansion: an implicit token is not the same as the token.

For \IfBeginWith the situation is a bit more complicated, but essentially the test fails because something like \edef\first{\TokenIsIn} is performed as well and, in \edef, \TokenIsIn is not changed into a.

With \ifthen and \IfBeginWith only explicit token should be used.

The three tests return true if you do

\newcommand{\settok}[1]{\gdef\TokenIsIn{#1}}
2

From another angle, the stringstrings package is very slow and is primarily intended for regular expressions. But if your problem is of that style, then it has some useful routines:

\documentclass{article}

\usepackage{stringstrings}

\begin{document}
\def\mystring{dcbadcbadcba}

\findchars{\mystring}{a} a's found in string

Position \whereischar{\mystring}{a} is where the first `a' is found

\testmatchingchar{\mystring}{3}{a}
Character 3 \ifmatchingchar is \else is not \fi an `a'. 

\testmatchingchar{\mystring}{4}{a}
Character 4 \ifmatchingchar is \else is not \fi an `a'. 
\end{document}

enter image description here

I see some stray spaces are being produced by the \whereischar macro

2

\IfBeginWith uses this code (shown by \meaning\IfBeginWith)

\long macro:->\@ifstar {\let \@xs@assign \@xs@expand@and@detokenize
\@xs@IfBeginWith@@ }{\let \@xs@assign \@xs@expand@and@assign \@xs@IfBeginWith@@

i.e. it does expansion and detokenization of the input code \TokenIsIn, but \if... does expansion until it finds non-expandable tokens, which is the case (at least I think so) with \global\let\...=#1, which isn't expandable and it can't evaluate to be true (or 'No' here).

However, using a \newtoks register and expanding (or better: making it expandable) it within \TokenIsIn with \the\MyToken does not keep \if... from expanding the construct.

See the example output for the letter b, which should yield No for all macros.

\documentclass{article}
\usepackage{xifthen, xstring}

\newcommand{\IfOne}[3]{%
   \if\TokenIsIn#1
   #2%
   \else
   #3%
   \fi
}

\newcommand{\IfTwo}[3]{%
  \ifthenelse{\equal{\TokenIsIn}{#1}}{#2}{#3}%
}

\newcommand{\IfThree}[3]{%
  \IfBeginWith{\TokenIsIn}{#1}{#2}{#3}%
}

\newcommand{\TokenIsIn}{\the\MyToken}

\newtoks\MyToken

\newcommand{\settok}[1]{\MyToken={#1}}%

\settok{a}


\begin{document}



\IfOne{a}{Yes}{No}

\IfTwo{a}{Yes}{No}

\IfThree{a}{Yes}{No}

\IfOne{b}{Yes}{No}

\IfTwo{b}{Yes}{No}

\IfThree{b}{Yes}{No}


\end{document}

enter image description here

2
  • Sorry, I might sound naive as I am just a beginner to tex. Does this mean that \let is not a reliable way to assign variables? Or is it that the commands in xifthen and xstring packages are not robust?
    – Miheer
    Apr 14, 2016 at 19:09
  • 1
    @Miheer: \let\... is good for assigning macros to each other, making a copy of the macro thereby (there are other applications too), but I doubt its usefulness for tokens. (tokens are no macros)
    – user31729
    Apr 14, 2016 at 19:12

You must log in to answer this question.

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