1

I'm working on modernizing and adapting an old LuaLaTeX template made for exams and other university documents. The thing is, the document is full of conditionals like these:

\newcommand{\showMarks}{Yes} % This is where the user is supposed to toggle marks

% ... later in the document ...
\ifthenelse{
    \equal{\showMarks}{Yes} \OR \equal{\showMarks}{yes} \OR \equal{\showMarks}{Si}
    \OR{} \equal{\mostrarPuntos}{Sí} \OR{} \equal{\mostrarPuntos}{si}  % and so on
}
{\pointformat{\bfseries(\themarginpoints)}} % if
{\pointformat{}} % else

(Note that I use several OR's because it needs to handle both Spanish and English)

As you can see, the template uses \newcommand to define toggles and then applies the transformations using \ifthenelse (from the ifthen package).

I find this solution inelegant because:

  • I have to repeat the conditional (yes/no) for every part of the document, and the syntax is really awful.
  • I have to state this for every particular toggle. (Currently more than 12)
  • I get weird spacing problems with two or more of these conditionals: it always gives me two spaces in between.
  • The ifthen package seems practically obsolete. (Checking this answer)

How can I rewrite this using more modern packages (like etoolbox) while abstracting the details?

Note that my main concern is elegance, not speed. My builds are pretty fast as they are, and my main concern is streamlining the document to make it more clean and maintainable.

11
  • “The ifthen package seems obsolete.” [citation needed] May 25 '20 at 0:11
  • Personally, I'd go for allowing just [Yy]es. You want the code to be fast or to be elegant? (Though ifthen is not obsolete, and the code is pretty fast already, even with all those tests.) You can probably test once then set some internal conditional, like \ifthenelse{<many tests>}{\let\ifshowmarks\iftrue}{\let\ifshowmarks\iffalse}, then just use \ifshowmarks. May 25 '20 at 0:11
  • Welcome to TEX.SE! It would be great to have a minimal working example in order to clarify what do you want. As you mentioned, try tou use etoolbox (see p. 25: osl.ugr.es/CTAN/macros/latex/contrib/etoolbox/etoolbox.pdf). May 25 '20 at 0:12
  • @HenriMenke I've clarified that, thank you. May 25 '20 at 0:13
  • @SebastiánV.Romero I did try using etoolbox, but I couldn't find any way to compound the conditionals effectively (ORs). Regarding a MWE, I'm not sure what should I exemplify. May 25 '20 at 0:14
3

Personally, I'd go for allowing just yes (perhaps case insensitive). I find it odd that I can define a macro named \showMarks to expand to . It seems like a strange mixture.

Opinions aside, ifthen is not obsolete, and it provides a really fast interface for conditionals like that, so if you do that test multiple times it's a strong contender1.


Though there are other fast options as well. There's etoolbox's \ifboolexpr, which is similar to ifthen, but provides a different parsing mechanism. You can rewrite your tests like:

\ifboolexpr{%
  test {\ifstrequal{#1}{yes}}
  or
  test {\ifstrequal{#1}{si}}
  or
  ...
}

though you would still need to care for different cases, and accents (who defines a boolean option with an accent anyways ;-).

To work around the different cases, you can use \MakeLowercase on \showMarks and then compare with the lowercase versions, so you can get as short as:

\ifboolexpr{%
  test {\ifstrequal{#1}{yes}}
  or
  test {\ifstrequal{#1}{si}}
  or
  test {\ifstrequal{#1}{s\IeC{\'\i}}}
  or
  test {\ifstrequal{#1}{s\IeC{\'i}}}% For SÍ
}

though probably not too shorter than that. This is the fastest (after ifhten) option, since the case changing uses TeX's \lowercase. MWE #1 below implements this version.


If you prefer a less wordy syntax, you can try expl3's \str_case:nn (thanks Henri for reminding me of this one :-). You can use \str_case:nnTF and pass it a list of strings for it to check against, and then branch accordingly if any test matches. Also, thanks to expl3's case-changing, the test gets as short as:

\str_case:nnTF {#1}
  {
    {yes} { }
    {si}  { }
    {sí}  { }
  }
  { \prg_return_true: }
  { \prg_return_false: }

Besides, since both case-changing and string tests are expandable in expl3, you can write the entire thing as an expandable test. MWE #2 below implements this. The conditional in this version is a lot simpler than the previous, so it is much faster, but the entire code may be a bit slower because the case-changing uses a macro-based approach, rather than \lowercase. But the case-changing is much better because it “just works” with Unicode characters like í.


If you want the shortest possible code, you can use l3regex. I doubt something can get shorter than that. You just have to define a regex like:

\regex_const:Nn \c_augustin_yes_regex { (?i)^yes|s(i|í|Í)$ }

and then test with \regex_match:NnTF. However since this does a lot more than just comparing strings, this is slower than the other options (with slower I mean you'll have to run the test thousands of times to notice), but if you want to get really fancy with your tests this allows you to extend a lot. MWE #3 implements this.


MWE #1

\documentclass{article}
\usepackage{etoolbox}
\newrobustcmd\IfShowMarksTF{%
  \MakeLowercase{\gdef\noexpand\showmarkstest{\showMarks}}%
  \expandafter\ifshowmarksaux
    \expandafter{\showmarkstest}}%
\newrobustcmd\ifshowmarksaux[1]{%
  \ifboolexpr{%
    test {\ifstrequal{#1}{yes}}
    or
    test {\ifstrequal{#1}{si}}
    or
    test {\ifstrequal{#1}{s\IeC{\'\i}}}
    or
    test {\ifstrequal{#1}{s\IeC{\'i}}}% For SÍ
    }}
\ExplSyntaxOff

\begin{document}

\def\test#1{%
  \def\showMarks{#1}%
  #1: \IfShowMarksTF{true}{false}\par}

\test{yes}
\test{Yes}
\test{YES}
\test{yEs}
\test{si}
\test{SI}
\test{Sí}
\test{sÍ}
\test{no}
\test{sim}

\end{document}

MWE #2

\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewExpandableDocumentCommand \IfShowMarksTF { m m }
  {
    \exp_args:Ne \augustin_if_yes:nTF
      { \text_lowercase:n { \showMarks } }
      {#1} {#2}
  }
\prg_new_conditional:Npnn \augustin_if_yes:n #1 { p, T, F, TF }
  {
    \str_case:nnTF {#1}
      {
        {yes} { }
        {si}  { }
        {sí}  { }
      }
      { \prg_return_true: }
      { \prg_return_false: }
  }
\ExplSyntaxOff

\begin{document}

\def\test#1{%
  \def\showMarks{#1}%
  #1: \IfShowMarksTF{true}{false}\par}

\test{yes}
\test{Yes}
\test{YES}
\test{yEs}
\test{si}
\test{SI}
\test{Sí}
\test{sÍ}
\test{no}
\test{sim}

\end{document}

MWE #3

\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\regex_const:Nn \c_augustin_yes_regex { (?i)^yes|s(i|í|Í)$ }
\NewDocumentCommand \IfShowMarksTF { m m }
  {
    \exp_args:NNV \regex_match:NnTF
      \c_augustin_yes_regex \showMarks
      {#1} {#2}
  }
\ExplSyntaxOff

\begin{document}

\def\test#1{%
  \def\showMarks{#1}%
  #1: \IfShowMarksTF{true}{false}\par}

\test{yes}
\test{Yes}
\test{YES}
\test{yEs}
\test{si}
\test{SI}
\test{Sí}
\test{sÍ}
\test{no}
\test{sim}

\end{document}

All test files print:

enter image description here


1 Though if you do that test many times it's probably better if you test once, and then store the result of the test somehow, like:

\newif\ifshowmarks
\ifthenelse{<many tests>}%
  {\showmarkstrue}%
  {\showmarksfalse}%
% then
\ifshowmarks
  <do stuff>
\else
  <other stuff>
\fi
2
  • 1
    Why not \str_case:nn? May 25 '20 at 1:30
  • 1
    @HenriMenke Is “because I forgot” a valid answer? ;-) I'll edit May 25 '20 at 1:31

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.