0

This question already has an answer here:

There are only two clause in \ifthenelse—<then clause> and <clause>. If more than two test conditions are needed, the \ifthenelse has to be nested. This will make code awful and be hard to maintain. Is there a better way, for example, switch-case marco like this(like C language):

\switchconditon
  {<case 1> <clause 1>}
  {<case 2> <clause 2>}
  {<case 3> <clause 3>}

an example by \ifthenelse:

\ifthenelse{\equal{#1}{ul}}%ul=upper left
        {\let\position\AtPageUpperLeft}
        {}
    \ifthenelse{\equal{#1}{ll}}%ll=lower left
        {\let\position\AtPageLowerLeft}
        {}
    \ifthenelse{\equal{#1}{pc}}%pc=paper center
        {\let\position\AtPageCenter}
        {}

Can you give a code to achive the last code function? Thank you.

marked as duplicate by Werner, Stefan Pinnow, Troy, Schweinebacke, David Carlisle Aug 14 '17 at 12:36

This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.

8

The expl3 programming layer of LaTeX3 comes with a lot of utility functions for such purposes. The model offers different datastructures and algorithms which operate on them. Some of them have a switch statement, see examples below.

For your use-case the \str_case:nn is appropriate. If no match is found an error is signalled.

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn

\msg_new:nnn
  { switchcase }
  { no-match }
  { There~is~no~entry~`#1'~in~the~switch~statement! }

\NewDocumentCommand \switchcase { m m }
  {
    \str_case:nnF { #1 } { #2 } { \msg_error:nnn { switchcase } { no-match } { #1 } }
  }

\ExplSyntaxOff

\newcommand\whatever[1]{%
  \switchcase{#1}%
  {
    {ul}{\let\position\AtPageUpperLeft}
    {ll}{\let\position\AtPageLowerLeft}
    {pc}{\let\position\AtPageCenter}
  }%
}

% Sentinel
\newcommand*\AtPageUpperLeft{AtPageUpperLeft}
\newcommand*\AtPageLowerLeft{AtPageLowerLeft}
\newcommand*\AtPageCenter   {AtPageCenter}


\begin{document}

\whatever{ul}\position

\whatever{ll}\position

\whatever{pc}\position

\whatever{rubbish}\position % error!

\end{document}

For macros you can use \tl_case:nn:

enter image description here

If you want to compare strings in a catcode agnostic fashion, use \str_case:nn

enter image description here

For integers there is \int_case:nn

enter image description here

For dimensions there is \dim_case:nn

enter image description here

  • 1
    Thank you Menke. The Package expl3 must be declared? And could you show me how to use it through my updated code example. – lyl Aug 14 '17 at 7:45
  • It seems a bit complicate. Anyway, many thanks again. I'll try to understand it. – lyl Aug 14 '17 at 10:15
3

Here is a fairly general switch case macro, based on expl3. The syntax is

\switchcondition[<type>]{<input>}[<other>]{<cases>}

where <type> is one among string, token, integer or dimen (default string), <input> is the argument to test against the cases, <other> is what to do if no case is matched, and <cases> is the list of cases in the form

{<case-1>}{<code-1>}
{<case-2>}{<code-2>}
[...]
{<case-n>}{<code-n>}

The macro is, by itself, fully expandable, but of course this feature can be exploited only if <other> and <code-k> are fully expandable for every case.

Here's an example.

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\NewExpandableDocumentCommand{\switchcondition}{O{string}mmO{}}
 {
  \use:c { lyl_#1_switch:nnn } { #2 } { #3 } { #4 }
 }

\cs_new:Nn \lyl_string_switch:nnn
 {
  \str_case:nnF { #1 } { #2 } { #3 }
 }
\cs_new:Nn \lyl_token_switch:nnn
 {
  \tl_case:nnF { #1 } { #2 } { #3 }
 }
\cs_new:Nn \lyl_integer_switch:nnn
 {
  \int_case:nnF { #1 } { #2 } { #3 }
 }
\cs_new:Nn \lyl_dimen_switch:nnn
 {
  \dim_case:nnF { #1 } { #2 } { #3 }
 }
\ExplSyntaxOff

\newcommand{\placement}[1]{%
  \switchcondition{#1}{
    {ul}{\let\position\AtPageUpperLeft}
    {ll}{\let\position\AtPageLowerLeft}
    {ur}{\let\position\AtPageUpperRight}
    {lr}{\let\position\AtPageLowerRight}
  }[\let\position\ERROR]%
}

% whatever these should do
\providecommand{\AtPageUpperLeft}{APUL}
\providecommand{\AtPageLowerLeft}{APLL}
\providecommand{\AtPageUpperRight}{APUR}
\providecommand{\AtPageLowerRight}{APLR}
\providecommand{\ERROR}{ERROR}

\newcommand{\foo}[1]{%
  \switchcondition[integer]{#1}{
    {1}{One}
    {20}{Twenty}
    {42}{The answer!}
  }[Uninteresting number]%
}

\begin{document}

\placement{ul}\texttt{\meaning\position}

\placement{ll}\texttt{\meaning\position}

\placement{ur}\texttt{\meaning\position}

\placement{lr}\texttt{\meaning\position}

\placement{xy}\texttt{\meaning\position}

\foo{1}

\foo{6*7}

\foo{3456}

\end{document}

enter image description here

  • Thank you egreg. I need to learn more about the package xparse. – lyl Aug 14 '17 at 10:00
2

You can also just let TeX's csname hash table do the lookup for you so

\def\positionul{\let\position\AtPageUpperLeft}
\def\positionll{\let\position\AtPageLowerLeft}
\def\positionpc{\let\position\AtPageCenter}

then in the macro that you did not show, that wants to use #1 being ul, ll or pc just use

\csname position#1\endcsname

and it will execute \positionll if #1 is ll

  • I think this is a good and simple solution. By the way, why the following code does not show "aaa"? \def\t1{aaa} \def\t2{bbb} \newcommand{\tttt}[1]{\csname t#1\endcsname} \tttt{1} – lyl Aug 14 '17 at 13:17
  • \def\t1{aaa} defines \t followed by 1 you would need \expandafter\def\csname t1\endcsname{aaa} to access a macro named t1 – David Carlisle Aug 14 '17 at 13:59
  • Thank you. I want to define a marco named "\t1" by input "expandafter\def\csname t1\endcsname{aaa}", then I want to get "aaa" by input \t1,but "aaa" did not show. why? – lyl Aug 15 '17 at 12:53
  • @lyl same reason \t1 is the same as \t 1 the two tokens \t and 1 to access a command with name t1 you would need \csname t1\endcsname – David Carlisle Aug 15 '17 at 13:08

Not the answer you're looking for? Browse other questions tagged or ask your own question.