7

A TUGboat article of 15 years ago mentions an \ifnot macro by David Kastrup whose implementation is:

\def\ifnot#1{#1\else
    \expandafter\expandafter\fi
    \iffalse\iftrue\fi}

However, this macro looks a bit weird when it is applied to an \ifSomething macro, i.e., \ifnot{\ifeof\stream}. For the purpose of making TeX code a bit more readable, it might be useful to have a macro which negates the "condition" itself, without the if prefix.

Here is a minimal example, which does not work.

\documentclass{standalone}
\makeatletter
\newif\if@to@be
\begin{document}
  \@to@be@true
  \if@not\@to@be@ Not to be! \fi
  \@to@be@false
  \if@not\@to@be@ Not to be! \fi
\end{document}
  • 2
    You know that e-TeX has \unless, right? – Joseph Wright Apr 6 '15 at 19:18
  • Not quite the same, \unless\if@I@miss@something but \unless\@I@miss@something{would also be a nice approximation} – Yossi Gil Apr 6 '15 at 19:21
  • 3
    the \ifnot macro is designed to be used with an \if the article you reference has an example \ifnot\iftrue The usage \if@not\@to@be@ isn't the intended usage and \@to@be@ hasn't been defined, so it's not clear what you expect it to do – David Carlisle Apr 6 '15 at 19:32
  • 1
    What about creating a new \newif (e.g., named \newifnot) so, instead of creating the macros \iftobe, \iftobetrue and \iftobefalse it would create also \ifnottobe? I think it would be easier. And may be even appending the code to \newif itself. – Manuel Apr 6 '15 at 20:12
  • How would you link the boolean and it negation? It is easy to define both in the same command, but how would the setting of one of the two correlated variables effect a setting of the other to the negated value? – Yossi Gil Apr 7 '15 at 13:15
6

Possibly something like this? It preserves the OP's desired syntax \ifnot\tobe, while at the same time not demanding that \tobe be predefined. Additionally, for those who don't like using \tobe without defining it, it allows the alternate syntax \ifnot{tobe}, without any changes whatsoever.

\documentclass{article}
\makeatletter
% Following 3 lines thanks to Prof. Enrico Gregorio, from:
% http://tex.stackexchange.com/questions/42318/
% removing-a-backslash-from-a-character-sequence
\begingroup\lccode`\|=`\\
\lowercase{\endgroup\def\removebs#1{\if#1|\else#1\fi}}
\newcommand{\@macro@name}[1]{\expandafter\removebs\string#1}
%
\def\ifnot#1{%
  \edef\tmp{if\@macro@name{#1}}%
  \csname\tmp\endcsname\else
    \expandafter\expandafter\fi
    \iffalse\iftrue\fi}
\makeatother
\newif\iftobe
\begin{document}
  \tobetrue
  \ifnot\tobe Not to be! \else To be!\fi\par
  \tobefalse
  \ifnot\tobe Not to be! \else To be!\fi
\end{document}

enter image description here

  • \if@not\@to@be@ highlights the perspective that \@to@be@ is a Boolean variable. – Yossi Gil Apr 6 '15 at 19:25
  • 1
    @YossiGil Please see revision. – Steven B. Segletes Apr 6 '15 at 19:45
  • 1
    No, it won't work inside other conditionals; try \ifnum1>1\relax\ifnot\tobe Not to be! \else To be!\fi\fi – egreg Apr 6 '15 at 21:02
  • @egreg I have redacted the answer to reflect the insight. Thanks for the reality check. – Steven B. Segletes Apr 6 '15 at 22:30
6

Using e-TeX and assuming \escapechar is printable and not a space:

\documentclass{standalone}
\makeatletter
\newif\if@to@be@
\def\if@not#1{%
  \expandafter\unless\csname
    \expandafter\expandafter\expandafter i%
    \expandafter\expandafter\expandafter f%
      \expandafter\@gobble\string#1\endcsname
}
\begin{document}
  \@to@be@true
  \if@not\@to@be@ Not to be! \fi
  \@to@be@false
  \if@not\@to@be@ Not to be! \fi
\end{document}

(The restriction on \escapechar can be lifted if required: see \cs_to_str:N in expl3.)

  • I would say you are fully expended! – Steven B. Segletes Apr 6 '15 at 19:32
  • 2
    In contrast to David Kastrup's macro, this can't be used inside other conditionals. – egreg Apr 6 '15 at 19:40
  • @egreg You mean \if... balancing? Well no, but that is implicit in wanting to use \@to@be@ in place of \if@to@be@ I'd say. (I would not use either approach myself in real code, but there we go.) – Joseph Wright Apr 6 '15 at 19:43
  • @egreg I'm mainly trying to show \unless here: I'd just use that directly (as intended) in for example \unless\ifeof for looping over a file. – Joseph Wright Apr 6 '15 at 19:54
4

Having the full conditional in the argument to \ifnot is essential for the macro to work, or it cannot appear in another conditional, because TeX keeps track of \if..., \else and \fi in skipped text.

Assuming the conditional \iftobe is defined, your macro should work like

\ifnot{tobe}Not to be\else To be\fi

Now let's try

\iftrue
  \ifnot{tobe}Not to be\else To be\fi
\fi

Instead of \iftrue think to any other test, for instance \ifdim\maxdimen>0pt, that returns true. This gives no problem, because the test is removed and \ifnot is expanded, resurrecting the \iftobe which will match the first \fi.

Now consider

\iffalse
  \ifnot{tobe}Not to be\else To be\fi
\fi

The test is false, so everything up to and including the matching \else (or \fi) is skipped. Well, there is \else, so To be\fi\fi remains in the input stream. Do you see the problem? There's one unmatched \fi.

Giving to a macro a name that starts with \if doesn't make it a conditional. Only control sequences that are \let to a primitive conditional count. So TeX doesn't consider \ifnot in the skipped text to be matched by \else or \fi.

You have to use a real conditional:

\newif\iftobe

\def\NOT#1{%
  TT\fi
  \csname if#1\endcsname\else
  \expandafter\expandafter\fi
  \iffalse\iftrue\fi
}

\tobetrue

\if\NOT{tobe}Not to be\else To be\fi

\tobefalse

\if\NOT{tobe}Not to be\else To be\fi

\bye

enter image description here

As an exercise, try

\iffalse\if\NOT{tobe}Not to be\else To be\fi\fi

and see that no error is raised.

The result is the same as

\newif\iftobe

\def\ifnot#1{#1\else
  \expandafter\expandafter\fi
  \iffalse\iftrue\fi
}

\tobetrue

\ifnot{\iftobe}Not to be\else To be\fi

\tobefalse

\ifnot{\iftobe}Not to be\else To be\fi

\bye

Of course, David Kastrup's macro is much more powerful, because you can use any conditional in the argument, for instance

\ifnot{\ifdim\maxdimen>0pt}TRUE\else FALSE\fi

will print FALSE.

Of course, with e-TeX it's easier:

\unless\ifdim\maxdimen>0pt TRUE\else FALSE\fi

would do the same.


An implementation that lifts the restrictions, but is simply useless, in my opinion. The trick is to make \tobe equivalent to \iffalse, so it will count when \ifnot\tobe constructions are in the skipped text of a conditional. Of course, using \tobe in the wild is not recommended. ;-)

\documentclass{article}
\usepackage{expl3}

\ExplSyntaxOn

\cs_new_protected:Npn \newifnegatable #1
 {
  \exp_args:Nc \newif { if \cs_to_str:N #1 }
  \cs_set_eq:Nc #1 { if_false: }
 }

\cs_new:Npn \ifnot #1
 {
  \use:c { if \cs_to_str:N #1 }
  \else:
  \exp_after:wN \exp_after:wN \fi:
  \if_false: \if_true: \fi:
}

\ExplSyntaxOff

\newifnegatable\tobe

\begin{document}

\tobetrue

\ifnot\tobe Not to be\else To be\fi

\iftrue\ifnot\tobe Not to be\else To be\fi\fi

\iffalse\ifnot\tobe Not to be\else To be\fi\fi

\tobefalse

\ifnot\tobe Not to be\else To be\fi

\iftrue\ifnot\tobe Not to be\else To be\fi\fi

\iffalse\ifnot\tobe Not to be\else To be\fi\fi

\end{document}

enter image description here

A perhaps more useful implementation (but \unless is much easier anyway):

\documentclass{article}
\usepackage{etoolbox}

\newcommand{\newdoubleboolean}[1]{%
  \newbool{#1}\newbool{not#1}%
  \csappto{#1true}{\setbool{not#1}{false}}%
  \csappto{#1false}{\setbool{not#1}{true}}%
  \setbool{#1}{false}%
}

\newdoubleboolean{tobe}

\begin{document}

\tobetrue

\ifnottobe Not to be\else To be\fi

\iftobe To be\else Not to be\fi

\tobefalse

\ifnottobe Not to be\else To be\fi

\iftobe To be\else Not to be\fi

\end{document}

enter image description here

3

My solution redefines \newif macro. The new \newif\iffoo declares \iffoo and \ifnotfoo testing macros and (as usually) \footrue and \foofalse setting macros. Of course, no packages are needed. The testing macros are skipable in nested \if..\fi constructions.

\def\sdef#1{\expandafter\def\csname#1\endcsname}  
\def\newif#1{\expandafter\newifA\string#1\relax#1}
\expandafter\def\expandafter\newifA\string\if#1\relax#2{%
   \expandafter\newifB\csname ifnot#1\endcsname#2{#1}}
\def\newifB#1#2#3{% #1=\ifnotfoo, #2=\iffoo, #3=foo
   \sdef{#3true}{\let#2=\iftrue \let#1=\iffalse}% 
   \sdef{#3false}{\let#2=\iffalse \let#1=\iftrue}%
   \csname #3false\endcsname
}

%% test:
\newif\iftobe

\tobetrue
\iftobe YES\else NO\fi   
\ifnottobe NO\else YES\fi

\tobefalse
\iftobe YES\else NO\fi   
\ifnottobe NO\else YES\fi
2

Here's another idea.

\documentclass{scrartcl}
\usepackage{etoolbox}
\makeatletter
\newcommand\gobblethree[3]{} % poor hackish solution expecting usual escapechar
\newcommand*\newifnot[1]
 {\newif#1%
  \csappto{\expandafter\gobblethree\string#1true}%
    {\cslet{\expandafter\newifnotaux\string#1}\iffalse}%
  \csappto{\expandafter\gobblethree\string#1false}%
    {\cslet{\expandafter\newifnotaux\string#1}\iftrue}%
  \csuse{\expandafter\gobblethree\string#1false}}
\newcommand*\newifnotaux[3]{ifnot}

\newifnot\iftobe
\begin{document}
  \tobetrue
  \ifnottobe Not to be! \fi
  \tobefalse
  \ifnottobe Not to be! \fi
\end{document}

You could even append code to \newif macro so you have all your \ifnots automatically defined.

  • How about a version of your \newifnot whose name is \new@boolean, or if you prefer the more standard convention, \newboolean. The \new@boolean@` command could use the \removebs (see @Steven B. Segletes answer) macro to define a new boolean variable, allowing, e.g., \new@boolean\@tobe@, and then \@tobe@true, \@bobe@false, \if@tobe@ and \if@not@tobe@? – Yossi Gil Apr 7 '15 at 17:52
  • That's exactly what it does, except that \if@not@tobe@ is \ifnot@tobe@. If you want to have a @ between if and not, just edit \newifnotaux. It would be much easier if you used \newwhatever{@tobe@} rather than \newwhatever\if@tobe@ (which is exactly what the last edit of egreg does), but I used the same syntax of \newif\if@tobe@. – Manuel Apr 7 '15 at 18:23

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.