76

I feel really stupid for asking this, but how do you form more complex if conditionals in TeX? I'm looking for something like:

\ifnum\x=1 OR \ifnum\x=14
    {do this}
\else
    {do that}
\fi

I don't want to have to resort to copy-pasting the entire condition just to change the expression when the body is the same.

raphink
  • 31,894
gablin
  • 17,006

8 Answers8

57

There are a number of approaches. Assuming you are looking for a purely primitive-based on, then something like

\ifnum\ifnum\x=1 1\else\ifnum\x=14 1\else0\fi\fi
   =1 %
   <do this>
 \else
   <do that>
 \fi

Thus you use 'secondary' conditionals to convert the original problem into a simple TRUE/FALSE situation, where the 'outer' \ifnum is simply testing for 0 or 1. (This works as TeX keeps expanding until it finds a number when considering the outer \ifnum.)

It's important to get the number-termination correct when using this approach. In the example, the spaces after \x=1 and \x=14 are required to get the correct outcome. With a bit more imagination, you can make more complex constructs using the same approach (for example, you can having combined OR and AND conditions in this way.)

An alternative method if the logic gets complex would be to include the 'payload' as separate macros:

\ifnum\x=1 %
  \expandafter\myfirstcase
\else
  \ifnum\x=14 %
    \expandafter\expandafter\expandafter\myfirstcase
  \else
    \expandafter\expandafter\expandafter\mysecondcase
  \fi
\fi
\def\myfirstcase{do this}
\def\mysecondcase{do that}

This is what you often see with larger 'to do' blocks. The \expandafter use is 'good practice' but may not be needed depending on the exact nature of the code to insert.

Joseph Wright
  • 259,911
  • 34
  • 706
  • 1,036
  • 9
    Yes, TeX is Turing-complete, but... – raphink Sep 29 '11 at 12:27
  • 7
    @Raphink There are alternative, logic-based, approaches. However, the question says [tag:tex-core], so I went with a pure primitive approach. (I'd personally use LaTeX3's \bool_if:nTF.) – Joseph Wright Sep 29 '11 at 12:28
27
\usepackage{etoolbox}

\newcommand{\mytest}[1]{%
  \ifboolexpr{ test {\ifnumcomp{#1}{=}{1}} or test {\ifnumcomp{#1}{=}{14}} }
    {do this}
    {do that}}

\mytest{1} \mytest{14} \mytest{0}

There is also the xifthen package that provides for "composite" tests.

egreg
  • 1,121,712
  • 2
    I like this answer, because the longer a document gets, the more likely it's going to use the etoolbox package (either directly or via some other package). So chances are this is available without extra packages. – Matthew Leingang Apr 27 '18 at 16:40
8

The package xintexpr implements boolean logic on arithmetic expressions. We can use therein the \pdfstrcmp utility (if the engine makes it available) to compare strings.

\documentclass{article}

\usepackage{xintexpr}

\begin{document}

\def\x{14}

\xintifboolexpr { \x = 1 || \x = 14 }
  {True}
  {False}

\xintifboolexpr { even(\x) && \x < 24 }
  {True}
  {False}

\xintifboolexpr { \x = floor(sqrt(\x))^2 }
  {True}
  {False}

% \pdfstrcmp {text1}{text2} evaluates to 0 if text1 and text2 are equal
% to -1 if text1 comes first in lexicographic order, to +1 else
% To test if the strings are equal we thus use not(\pdfstrcmp {text1}{text2})
% (or we can use the ! as synonym of the "not" function)

\xintifboolexpr { 1=1 && (2=3 || 4<= 4 || not(\pdfstrcmp {abc}{def})) && !(2=4)}
  {True}
  {False}

\xintifboolexpr {\pdfstrcmp {abc}{def} = -1}
  {True}
  {False}

\end{document}

Compiled with PDFLaTeX:

enter image description here

The package can also be used with Plain TeX.

As seen above && is AND and || is OR. It is also possible to write 'and' and respectively 'or' (quotes mandatory).

5

LaTeX3 provides testing of boolean expressions that allows for using infix notation && (for AND), || (for OR) and ! (for negation). In addition to this, parentheses can be used to isolate sub-expressions.

\documentclass{article}

\usepackage{xparse}

\begin{document}

\def\x{14}

\ExplSyntaxOn
\bool_if:nTF { \int_compare_p:n {\x = 1} || \int_compare_p:n {\x = 14} }
  { True }
  { False }
\ExplSyntaxOff

\end{document}

The following (more complex) example was taken from section VI.3 Boolean expressions of the LaTeX3 interfaces documentation:

\int_compare_p:n { 1 = 1 } &&
  (
    \int_compare_p:n { 2 = 3 } ||
    \int_compare_p:n { 4 <= 4 } ||
    \str_if_eq_p:nn { abc } { def }
  ) &&
! \int_compare_p:n { 2 = 4 }
Werner
  • 603,163
3

With pgfmath (TikZ):

enter image description here

\documentclass{article}
\usepackage{pgfmath}
\begin{document}
\newcommand\OrTest[1]{%
\pgfmathparse{#1==14 || #1==1 ? "#1 is equal to 1 or 14." :  "#1 is not equal to 1 or 14."}%
\pgfmathresult}

\OrTest{14}

\OrTest{123} \end{document}

If you want more than a text-output you can use it as a true/false-tester like this:

enter image description here

\documentclass{article}
\usepackage{tikz}
\begin{document}
\foreach \No in {-3,0,1,2,3,7,11,14,123,1,7,999}{%%
\pgfmathparse{\No==1 || \No==14 ? 1 :  0}
\ifnum\pgfmathresult=1 \colorbox{blue!33}{\No}~%
 \else%
 \colorbox{red!33}{\No}~%
\fi}%%
\end{document}
cis
  • 8,073
  • 1
  • 16
  • 45
0

With functional package (which provides functional LaTeX2 interface for expl3) you can write the following code:

\documentclass{article}

\usepackage{functional}

\begin{document}

\IgnoreSpacesOn

\TlSet \lTmpaTl {2} \BoolVarOrTF { \IntCompare {\lTmpaTl} = {1} } { \IntCompare {\lTmpaTl} = {14} } { \Result {True} } { \Result {False} }

\TlSet \lTmpaTl {14} \BoolVarOrTF { \IntCompare {\lTmpaTl} = {1} } { \IntCompare {\lTmpaTl} = {14} } { \Result {True} } { \Result {False} }

\IgnoreSpacesOff

\end{document}

L.J.R.
  • 10,932
0

You can inspire by the OpTeX trick 0019. Ful expandable conditionals with AND and OR combined by parentheses are described here. The solution is base only on TeX primitives, of course.

wipet
  • 74,238
  • I don't understand how \ifx aa gets expanded into IF a=a. Is a a macro? Is the \ missing? Should that be \ifx \a\a instead? I'm confused… – Atcold Mar 22 '22 at 17:02
  • The \ifx primitive gets two following tokens (unexpanded) and compares if they have the same meaning. The token a is equal to the token a, it means that they have the same meaning. So, the result of \ifx is true. See TeX in nutshell for summary of TeX primitives. – wipet Mar 22 '22 at 17:11
  • The token a is always going to be equal to the token a. So, is this an always true check? Is one supposed to swap aa for something else, when using the construct? – Atcold Mar 22 '22 at 17:22
  • 1
    The \ifx aa is an example which only shows that you can use arbitrary \if... construct. Keep the context of the whole OpTeX trick. – wipet Mar 22 '22 at 17:29
  • I understand. Thanks. – Atcold Mar 23 '22 at 17:58
0

Another way with only TeX primitives & simple macros.

If you don't restrict yourself to only expandable commands then the code can be readable... well, as readable as assembly since that's presumably how Knuth designed TeX.

\newif\iftmp  % can also use LaTeX's \if@tempswa but making your own temporary avoids clashes

\ifnum\x=1 \tmptrue \else \ifnum\x=14 \tmptrue \else \tmpfalse \fi \fi

\iftmp <do this> \else <do that> \fi

(*) if you are used to reading \expandafter etc. then the other way isn't that bad.
(**) input-stream-parsing codes is still quite unreadable regardless.

user202729
  • 7,143