A more elegant version of the \ifnot macro
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}
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
.)
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
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}
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}