2

I'm trying to manipulate multiple boolean variables which are simply named one, two, three, etc. and since I'm using them with a counter, I have defined some commands to take a numerical input and handle the boolean values:

\newif\ifp@one@
\newif\ifp@two@
\newif\ifp@three@

\def\p@settrue@#1{
  \ifnum#1 =1 \p@one@true \else
  \ifnum#1 =2 \p@two@true \else
  \ifnum#1 =3 \p@three@true
  \fi\fi\fi}

\def\p@setfalse@#1{<same as with true>}

\def\p@if@#1{
  \ifnum#1 =1 \expandafter\ifp@one@ \else
  \ifnum#1 =2 \expandafter\ifp@two@ \else
  \ifnum#1 =3 \expandafter\ifp@three@
  \fi\fi\fi}

The first two commands work fine, so if I run \p@settrue@1, then \ifp@one@ is now true.

The problem if with the third function, \p@if@: I can't get it to work. I thought that it would simply be a matter of adding \expandafter before each \ifp@<number>@ but this doesn't work.

Ideally, I was hoping to use it like

\p@if@1
   <do something>
\else
   <do something else>
\fi

Why does this not work?


I realize I can instead define

\def\p@if@#1#2#3{
  \ifnum#1 =1 \ifp@one@#2\else#3\fi \else
  \ifnum#1 =2 \ifp@two@#2\else#3\fi \else
  \ifnum#1 =3 \ifp@three@#2\else#3\fi 
  \fi\fi\fi}

which is a nice alternative, but I'm still curious why the code above fails.

6
  • 2
    This seems to call for \ifcase
    – egreg
    Commented May 21, 2015 at 11:21
  • I was about to make same remark as @egreg. Besides, there is one thing I don’t understand: are \ifp@one@, etc., themselves conditionals? If so, they aren’t properly nested.
    – GuM
    Commented May 21, 2015 at 11:32
  • I didn't know about \ifcase, but by the sounds of it, it only executres one branch, and ultimately, I still need to have the boolean variables defined somewhere and accessible. Also, @GustavoMezzetti, that was a typo... I shall ammend that quickly.
    – JP-Ellis
    Commented May 21, 2015 at 11:38
  • Suppose the first \ifnum inside \p@if@ evaluates to false: then TeX will skip subsequent tokens, but tracking nested conditionals as it goes. When it comes to \ifp@one@, it thinks: “this is a conditional, so the next \else I find will pertain to it”; and indeed, when the following \else comes along, it will be paired with the \ifp@one@ just seen. At this point, TeX and your intentions are already out of sync. :-)
    – GuM
    Commented May 21, 2015 at 11:55
  • By why does \expandafter not work in this case?
    – JP-Ellis
    Commented May 21, 2015 at 12:04

2 Answers 2

3

Set up a translation table from number to textual representation:

\catcode`@=11

\def\p@translate#1{%
  \ifcase#1\or one\or two\or three\fi
}
\def\p@settrue@#1{%
  \csname p@\p@translate{#1}@true\endcsname
}
\def\p@setfalse@#1{%
  \csname p@\p@translate{#1}@false\endcsname
}
\def\p@cond@#1{%
  TT\fi
  \csname ifp@\p@translate{#1}@\endcsname
}

\newif\ifp@one@
\newif\ifp@two@
\newif\ifp@three@

% Testing
\def\msg#{\immediate\write16}
\p@settrue@{1}
\if\p@cond@{1}\msg{1 = true}\else\msg{1 = false}\fi
\if\p@cond@{3}\msg{3 = true}\else\msg{3 = false}\fi

\bye

Since it's impossible to have a macro that behaves correctly like conditionals in skipped texts, the TT\fi trick is used, so you call

\if\p@cond@{1}%
  <text to be executed if conditional 1 is true>%
\else
  <text to be executed if conditional 1 is false>%
\fi

Output:

1 = true
3 = false
1
  • Very neat, and this can be quite naturally extended :)
    – JP-Ellis
    Commented May 21, 2015 at 13:17
6

The definition of \p@if@ does not work as expected, because of the nesting \if constructs. Much more of \expandafter are needed to clean up the closing \else and \fi tokens.

The following example uses a \csname construct to ease the clean up and changes the syntax. \iftrue precedes \p@if@. The latter is a macro and it is not recognized by TeX as part of the \if commands. When TeX skips over branches, then it does not inspect macros. The construct \iftrue\p@if@ can also be used inside \if constructs.

\catcode`\@=11

\newif\ifp@one@
\newif\ifp@two@
\newif\ifp@three@

\def\p@settrue@#1{
  \ifcase#1\or
    \p@one@true\or
    \p@two@true\or
    \p@three@true
  \fi
}
\def\p@setfalse@#1{
  \ifcase#1\or
    \p@one@false\or
    \p@two@false\or
    \p@three@false
  \fi
}

\def\p@if@#1{%
  \fi
  \csname if%
    \ifcase#1false\or
      \ifp@one@ true\else false\fi\or
      \ifp@two@ true\else false\fi\or
      \ifp@three@ true\else false\fi\else
      false%
    \fi
  \endcsname
}

% Testing
\def\msg#{\immediate\write16}
\p@settrue@1
\iftrue\p@if@1\msg{1 = true}\else\msg{1 = false}\fi
\iftrue\p@if@3\msg{3 = true}\else\msg{3 = false}\fi

\csname @@end\endcsname\end

Result:

1 = true
3 = false

You must log in to answer this question.

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