TransWikia.com

Implementing switch cases

TeX - LaTeX Asked by Count Zero on January 17, 2021

I’d like to have command built around a switch-case environment in LaTeX, much like the example below:

documentclass{article}

usepackage{xstring}

newcommand{dothis}[1]{%
IfStrEqCase{#1}{{a}{so you typed a}
    {b}{now this is b}
    {c}{you want me to do c?}}
    [nada]
}

begin{document}

dothis{a}

dothis{b}

dothis{c}

dothis{e}

end{document}

My problem with it is that it requires the xstring package. Is there any other way to do this? Preferably without loading additional packages and avoiding disgraceful thickets of if-else-fi statements?

8 Answers

The question asks to avoid packages, so while this is the method used by expl3 in str_case:nnF I have recoded it with minimal support. The only package I've used is pdftexcmds, which is needed as the pdfstrcmp primitive from pdfTeX is called strcmp by XeTeX and has to be implemented in Lua for LuaTeX. This is easy enough to do without the package, but obscures the method: ask a separate question if required!

The general idea here is to set up a comparison loop in which the test is done expandably by pdfstrcmp. The test string is passed every time, with the 'true' string inserted if there is a match by the 'tidy up' code. If there is no match at all then the 'else' code is inserted. The romannumeral business means that it always requires exactly two expansions to do the work here:

documentclass{article}
usepackage{pdftexcmds}
makeatletter
newcommand*{dothis}[1]{%
  stringcases
    {#1}%
    {%
      {a}{so you typed a}%
      {b}{now this is b}%
      {c}{you want me to do c?}%
    }%
    {[nada]}%
}
newcommand{stringcases}[3]{%
  romannumeral
    str@case{#1}#2{#1}{#3}q@stop
}
newcommand{str@case}[3]{%
  ifnumpdf@strcmp{unexpanded{#1}}{unexpanded{#2}}=z@
    expandafter@firstoftwo
  else
    expandafter@secondoftwo
  fi
    {str@case@end{#3}}
    {str@case{#1}}%
}
newcommand{str@case@end}{}
longdefstr@case@end#1#2q@stop{z@#1}
makeatother

begin{document}

dothis{a}

dothis{b}

dothis{c}

dothis{e}

end{document}

Correct answer by Joseph Wright on January 17, 2021

The following is from catoptions package. The following ifcasse is cptifcasse in catoptions package. I think Count Zero should simply load an existing package for his task.

documentclass{article}
usepackage{pdftexcmds}
makeatletter
longdefam@alltoendif#1endif{unexpanded{#1}}
longdefam@domatchcode#1#2endif{unexpanded{#1}}
longdefamifcond#1fi{csname @#1firstelse secondfi oftwoendcsname}
longdefamifstrcmp#1#2{%
  amifcondif0pdf@strcmp{detokenize{#1}}{detokenize{#2}}fi
}
defifcasse#1#2{%
  amifstrcmp{#1}ifnone{%
    am@alltoendif
  }{%
    amifstrcmp{#1}endif{}{%
      amifstrcmp{#2}ifnone{%
        am@alltoendif
      }{%
        amifstrcmp{#2}endif{}{am@ifcasse{#1}{#2}}%
      }%
    }%
  }%
}
defam@ifcasse#1#2#3{%
  amifstrcmp{#3}ifnone{%
    am@alltoendif
  }{%
    amifstrcmp{#3}endif{}{%
      #1{#2}{#3}am@domatchcode{am@ifcasse@i{#1}{#2}}%
    }%
  }%
}
defam@ifcasse@i#1#2#3{am@ifcasse{#1}{#2}}

% The user can also define his own logical test command and pass it as the first 
% argument of ifcasse. For example, we define ifnumtest:

defifnumtest#1#2{amifcondifnum#1#2fi}

% We rewrite Count Zero's macro:
newcommand*{dothis}[1]{%
  par
  ifcasseamifstrcmp{#1}%
    {a}{so you typed a}
    {b}{now this is b}
    {c}{you want me to do c?}
  ifnone
    [no match]%
  endif
}
makeatother

begin{document}
dothis{a}
dothis{b}
dothis{c}
dothis{e}

% Weird test that correctly gives x as 'empty':
edefx{%
  ifcasseamifstrcmp{x}
  endif
}
%showx

% Weird test that gives x as 'no match':
edefx{%
  ifcasseamifstrcmp{x}
  ifnone
    [no match]%
  endif
}
par``x''.

% Number test:
edefx{%
  ifcasseifnumtest{2}
    {=1}{equal to 2}
    {<3}{less than 3}
    {>4}{greater than 4}
  ifnone
    no match%
  endif
}
par``x''.
end{document}

Answered by Ahmed Musa on January 17, 2021

Though it's most likely too late for the OP, I just worked out my own switch and thought I'd share it here for future readers. My solution uses solely the package xifthen (ifthen suffices too, but I already had xifthen installed...).

% Switch implementation
usepackage{xifthen}
newcommand{ifequals}[3]{ifthenelse{equal{#1}{#2}}{#3}{}}
newcommand{case}[2]{#1 #2} % Dummy, so renewcommand has something to overwrite...
newenvironment{switch}[1]{renewcommand{case}{ifequals{#1}}}{}

% Example: Pick color by ID
newcommand{incolor}[2]{
    begin{switch}{#1}
        case{1}{color{red}}
        case{2}{color{blue}}
        case{3}{color{green}}
        case{4}{color{black}}
        #2
    end{switch}
}

This code compiles perfectly fine in my TeXMaker (ofc. you'll need the color-package for this example, but it's not part of the switch). The example colors a given input in a color defined by an ID I chose (Usage: incolor{ID}{Content}). I used it for shorthand notations of lots of things (e.g. lp1, lp2, ... for parentheses in different colors using newcommand{lp}[1]{incolor{#1}{langle}}). Feel free to experiment ;)

I could imagine this to be expanded to a solution using only built-in control structures, but I've been too lazy for now to do so, yet ifx and else should do the trick.

Answered by Thev on January 17, 2021

Here is a solution via macro definitions (a single test makes the choice):

documentclass{article}
makeatletter
newcommandaddcase[3]{expandafterdefcsnamestring#1@case@#2endcsname{#3}}
newcommandmakeswitch[2][]{%
  newcommand#2[1]{%
    ifcsnamestring#2@case@##1endcsnamecsnamestring#2@case@##1endcsnameelse#1fi%
  }%
}
makeatother

makeswitch[nada]dothis
addcasedothis{a}{so you typed a}
addcasedothis{b}{so you typed b}
addcasedothis{c}{you want me to do c?}

begin{document}
dothis{a}

dothis{b}

dothis{c}

dothis{e}

end{document}

Answered by Paul Gaborit on January 17, 2021

Based on the great answer by Thev and updated by MaxD I have implemented a default command for switch. I used ifthen package:

newboolean{default}
newcommand{case}{}
newcommand{default}{}

newenvironment{switch}[1]{%
    setboolean{default}{true}
    renewcommand{case}[2]{ifthenelse{equal{#1}{##1}}{%
        setboolean{default}{false}##2}{}}%
    renewcommand{default}[1]{ifthenelse{boolean{default}}{##1}{}}
}{}

Then implement it as such:

  begin{switch}{program}  
    case{2}{twodayprogram}  
    case{4}{fourdayprogram}  
    default{first last missing programpagebreak}  
  end{switch}

in case anyone is curious, this was a program for an off-season sports conditioning program and was being looped with datatool, the commands twodayprogram and fourdayprogram implement the program for a particular athlete

Answered by coachshea on January 17, 2021

In LuaTeX you can use Lua tables to emulate a switch statement.

% arara: lualatex
documentclass{article}

newcommand*dothis[1]{%
  directlua{
    local cases = {
        a = "so you typed a",
        b = "now this is b",
        c = "you want to do c?"
    }

    if cases["luaescapestring{#1}"] string~= nil then
        tex.sprint(cases["luaescapestring{#1}"])
    else
        tex.sprint("[nada]")
    end
  }
}

begin{document}

dothis{a}

dothis{b}

dothis{c}

dothis{d}

end{document}

enter image description here

Answered by Henri Menke on January 17, 2021

This is a simple extension to Thev's answer, adding support for a default case.

% Switch implementation
usepackage{xifthen}
newcommand{ifequals}[4]{ifthenelse{equal{#1}{#2}}{#3}{#4}}
newcommand{case}[2]{#1 #2} % Dummy, so renewcommand has something to overwrite...
newenvironment{switch}[1]{renewcommand{case}{ifequals{#1}}}{}

% Example: Pick color by ID
newcommand{incolor}[2]{
    begin{switch}{#1}
        case{1}{color{red}}{
        case{2}{color{blue}}{
        case{3}{color{green}}{
        case{4}{color{black}}{
        color{cyan}
        }}}}
    end{switch}
    #2
}

Answered by Zalastax on January 17, 2021

There is the built-in ifcase which works like a switch-case that interpret numbers. Because of this, I translated the strings 'a', 'b', 'c' to numbers 0, 1, 2. Note that the first case is 0. It don't need packages and is very simple.

documentclass{article}
newcommand{dothis}[1]{%
ifcase#1relax so you typed a % Case 0.

or now this is b % Case 1.

or you want me to do c? % Case 2.

else Default case.
fi
}
begin{document}
dothis{0}

dothis{1}

dothis{2}

dothis{3}
end{document}

Answered by Rodolfo FR on January 17, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP