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}
6
  • 2
    You know that e-TeX has \unless, right?
    – Joseph Wright
    Commented Apr 6, 2015 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
    Commented Apr 6, 2015 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 Commented Apr 6, 2015 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
    Commented Apr 6, 2015 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
    Commented Apr 7, 2015 at 13:15

5 Answers 5

7

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

4
  • \if@not\@to@be@ highlights the perspective that \@to@be@ is a Boolean variable.
    – Yossi Gil
    Commented Apr 6, 2015 at 19:25
  • 1
    @YossiGil Please see revision. Commented Apr 6, 2015 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
    Commented Apr 6, 2015 at 21:02
  • @egreg I have redacted the answer to reflect the insight. Thanks for the reality check. Commented Apr 6, 2015 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.)

4
  • I would say you are fully expended! Commented Apr 6, 2015 at 19:32
  • 2
    In contrast to David Kastrup's macro, this can't be used inside other conditionals.
    – egreg
    Commented Apr 6, 2015 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
    Commented Apr 6, 2015 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
    Commented Apr 6, 2015 at 19:54
5

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

5
  • What is the difference/advantage between \unless\ifdim...\fi and \ifdim\else...\fi?
    – divenex
    Commented Nov 16 at 15:44
  • @divenex \ifdim 1pt>2pt returns false, \unless\ifdim 1pt<2pt returns true.
    – egreg
    Commented Nov 16 at 17:44
  • Thanks @egreg. Note however, the \else in my comment: \unless\iftobe DoSomething\fi seems to have the same effect as \iftobe\else DoSomething\fi.
    – divenex
    Commented Nov 16 at 19:03
  • @divenex It's supposed to, doesn't it?
    – egreg
    Commented Nov 16 at 21:07
  • Thanks. Good to know. Just trying to learn...
    – divenex
    Commented Nov 16 at 22:51
4

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.

2
  • 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
    Commented Apr 7, 2015 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
    Commented Apr 7, 2015 at 18:23

You must log in to answer this question.

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