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}
5
  • 3
    \equal doesn't actually compare the meaning of \abc and \relax. It does \def\a{\abc}, \def\b{\relax}, then compares \a and \b, which contain different things, so are different. Mar 25 at 18:49
  • Ok, but how to get \ifx-like comparison inside ifthen? Mar 25 at 18:50
  • A work-around I used: define a boolean and before use \ifthenelse or \whiledo, I set the boolean with \ifx and use the boolean... but I loose ifthen practicality. Mar 25 at 18:55
  • The hands-down best solution to do that with ifthen is to not use ifthen :) Mar 25 at 18:57
  • It would be very handy extend ifthen creating a \equalx{\csA}{\csB} that tests expansion of command sequences \csA and \csB, thai is, a version of \ifx comparison. Mar 25 at 19:03

2 Answers 2

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}
    
2
  • Thanks! Second solution is perfect to my application. Mar 25 at 19:05
  • @MiguelV.S.Frasson Proceed with caution ;-) Mar 25 at 19:06
4

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

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.