4

I was implementing an ad hoc list data structure inside LaTeX, where \aaa expands value for aaa, \bbb exapnds value for bbb and to show that \bbb is next to \aaa on the list, \NEXTaaa expand to bbb, and I do not define \NEXTbbb. These definitions are done with \@namedef.

Using ifthen macros to work the code, I found a (supposedly) bug. I cannot compare \relax to macros \relax-like, like the one got after expansion of \csname UndefinedCmd\endcsname.

The question is: after \let\abc\relax, how to get a successfully compare \abc with \relax using ifthen macros? I am actually getting \equal{\abc}{\relax} -> false!!! Is this a bug on ifthen?

MWE:

\documentclass{article}
\usepackage{ifthen}

\begin{document}

\let\abc\relax

\ifthenelse{\equal{\abc}{\relax}} {Good!} {Bad :-(}

%% I read Bad :-( on PDF file.

\end{document}

2 Answers2

5

Not a drop-in replacement of \ifthenelse, but more powerful.

\documentclass{article}

\ExplSyntaxOn \NewExpandableDocumentCommand{\xifthenelse}{mmm} { \bool_if:nTF { #1 } { #2 } { #3 } }

\cs_new_eq:NN \numtest \int_compare_p:n \cs_new_eq:NN \oddtest \int_if_odd_p:n \cs_new_eq:NN \fptest \fp_compare_p:n \cs_new_eq:NN \dimtest \dim_compare_p:n \cs_new_eq:NN \deftest \cs_if_exist_p:N \cs_new_eq:NN \namedeftest \cs_if_exist_p:c \cs_new_eq:NN \eqdeftest \token_if_eq_meaning_p:NN \cs_new_eq:NN \streqtest \str_if_eq_p:ee \cs_new_eq:NN \emptytest \tl_if_empty_p:n \cs_new_eq:NN \blanktest \tl_if_blank_p:n \cs_new_eq:NN \boolean \legacy_if_p:n \cs_new:Npn \modetest #1 { \str_case:nnF { #1 } { {h}{\mode_if_horizontal_p:} {v}{\mode_if_vertical_p:} {m}{\mode_if_math_p:} {i}{\mode_if_inner_p:} } {\c_false_bool} } \cs_new:Npn \enginetest #1 { \str_case:nnF { #1 } { {luatex}{\sys_if_engine_luatex_p:} {pdftex}{\sys_if_engine_pdftex_p:} {ptex}{\sys_if_engine_ptex_p:} {uptex}{\sys_if_engine_uptex_p:} {xetex}{\sys_if_engine_xetex_p:} } {\c_false_bool} }

\ExplSyntaxOff

\begin{document}

\let\abc\relax

\xifthenelse{\eqdeftest{\abc}{\relax}}{true}{false}

\xifthenelse{\eqdeftest{\abc}{\relax} && \numtest{0>1}}{true}{false}

\xifthenelse{\eqdeftest{\abc}{\relax} || \numtest{0>1}}{true}{false}

\xifthenelse{\enginetest{pdftex} || \enginetest{luatex}}{true}{false}

\end{document}

You can use parentheses, && for “and”, || for “or”, ! for negation.

enter image description here

egreg
  • 1,121,712
4

You should probably not use ifthen. Maybe etoolbox's \ifboolexpr or something more robust. But if you must, here are two options.

  1. Redefine ifthen's \isundefined to consider \relax as undefined:

    \documentclass{article}
    \usepackage{ifthen}
    

    \makeatletter \def\TE@undef#1#2{% \TE@throw \noexpand\ifnum1=% \noexpand\ifx\noexpand@undefined\noexpand#11\noexpand\fi \noexpand\ifx\relax\noexpand#11\noexpand\fi\space#2} \makeatother

    \begin{document}

    \let\abc\relax

    \ifthenelse{\isundefined{\abc}} {Good!} {Bad :-(}

    \end{document}

  2. Add a \isdefequal to do an \ifx comparison (the arguments must be two single tokens):

    \documentclass{article}
    \usepackage{etoolbox}
    \usepackage{ifthen}
    

    \makeatletter \patchcmd\ifthenelse {\let\isundefined\TE@undef} {\let\isundefined\TE@undef \let\isdefequal\TE@isdefequal} {}{\FAILED} \long\def\TE@isdefequal#1#2#3{\TE@throw \noexpand\ifx\noexpand#1\noexpand#2#3} \makeatother

    \begin{document}

    \let\abc\relax

    \ifthenelse{\isdefequal{\abc}{\relax}} {Good!} {Bad :-(}

    \end{document}