2

I imagine this has to be asked somewhere but I've been searching for a while with no luck, so if there is an answer feel free to just direct me there.

I have a series of functions that all do basically the same thing, but with a variable number of inputs. I would like to call one function with one comma separated list of inputs, have the function find the length of the input and call the appropriate function with the appropriate inputs. Unfortunately I don't really understand the comma separated list syntax and can't find a good primer for it. An example of what I want (pseudocode):

\newcommand{\RandQ}[1]{
%Set \length to be the length of the input to \RandQ list
\renewcommand{\length}{length{#1}}
%Now call the appropriate function with the appropriate inputs
ifthenelse{\length = 5}{\RandFiveQ{#1}{#2}{#3}{#4}{#5}}{}
%Where #1, #2, #3, etc are the 1st, 2nd, 3rd, etc entries of the list from the input above
ifthenelse{\length = 4}{\RandFourQ{#1}{#2}{#3}{#4}}{}
ifthenelse{\length = 5}{\RandThreeQ{#1}{#2}{#3}}{}
ifthenelse{\length = 5}{\RandTwoQ{#1}{#2}}{}
}

Where \RandFiveQ, \RandFourQ, \RandThreeQ, \RandTwoQ are already defined functions that take 5, 4, 3, and 2 inputs respectively.

Example usage would be something like \RandQ{1, 2, 3, 4}

Which would return

\RandFourQ{1}{2}{3}{4}

which would then get parsed as the actual command \RandFourQ.

As per request I am including the code for \RandXQ commands. I will add that this project was in an effort to circumvent some bad code in a required cls file, as well as learn how to code in latex itself... so there might be some Rube-Goldberging in here... you have been warned :)

Essentually, I have pre-generated a randomized list of counters A1-AX (RandFourQ generated A1 through A4, and randomly assigned the numbers 1 through 4 to them... e.g. A1 = 3, A2 = 2, A3 = 1, A4 = 4), then for each RandXQ the first input is the answer order, then the following are the answer choices. If the first one is 3, it outputs the 3rd answer choice, and so I can randomize answer orders (for a multiple choice test for example). Worth noting there are much easier ways to do this, but the required cls file killed off most of those choices unfortunately.

%Syntax: For each "choice" You want to type The following:
%\RandMe{n}{A}{}
%\(n)choices
%\Rand(n)Q{\arabic{A1}}{Answer1}{Answer2}...{Answer n}
%\Rand(n)Q{\arabic{A2}}{Answer1}{Answer2}...{Answer n}
%.
%.
%.
%\Rand(#ofchoices)Q{An}{Answer1}{Answer2}...{Answer n}
%
%Where n is the number of questions choices there are Note that the number of choices "n" in the \Rand(n)Q must be spelled out with the first letter capitalized. Thus for 5 choices you would type "\RandFiveQ". 
%So for example with a question that has 4 choices of "2", "4", "17" and "No Answer" you would type:
%
%\RandMe{4}
%\fourchoices
%{\RandFourQ{\arabic{A1}}{2}{4}{17}{No Answer}}
%{\RandFourQ{\arabic{A2}}{2}{4}{17}{No Answer}}
%{\RandFourQ{\arabic{A3}}{2}{4}{17}{No Answer}}
%{\RandFourQ{\arabic{A4}}{2}{4}{17}{No Answer}}
%
%Currently supported are: \RandFiveQ, \RandFourQ, \RandThreeQ, \RandTwoQ.
%Also required is \usepackage{calc}, \usepackage{lcg}
%
\newcommand{\RandFiveQ}[6]
{
\ifthenelse{#1 = 1}{#2}{}
\ifthenelse{#1 = 2}{#3}{}
\ifthenelse{#1 = 3}{#4}{}
\ifthenelse{#1 = 4}{#5}{}
\ifthenelse{#1 = 5}{#6}{}
}

\newcommand{\RandFourQ}[5]
{
\ifthenelse{#1 = 1}{#2}{}
\ifthenelse{#1 = 2}{#3}{}
\ifthenelse{#1 = 3}{#4}{}
\ifthenelse{#1 = 4}{#5}{}
}

\newcommand{\RandThreeQ}[4]
{
\ifthenelse{#1 = 1}{#2}{}
\ifthenelse{#1 = 2}{#3}{}
\ifthenelse{#1 = 3}{#4}{}
}

\newcommand{\RandTwoQ}[3]
{
\ifthenelse{#1 = 1}{#2}{}
\ifthenelse{#1 = 2}{#3}{}
}
5
  • 1
    It would be a little easier if we would know what \RandXQ actually does. I think, this could be done with \clist features and \clist_map_function from expl3
    – user31729
    Commented Oct 12, 2015 at 21:47
  • Added the definitions of \RandXQ, also the \RandMe{n}{A}{} just generate some random number counters to be used as dummy inputs currently and can be fairly ignored if you assume \arabic{A1}, \arabic{A2}, etc are really the numbers 1, 2, etc.
    – Jason
    Commented Oct 12, 2015 at 22:01
  • So, \RandFiveQ actually takes 6 arguments, \RandFourQ takes 5 arguments, etc? Please confirm.
    – Mico
    Commented Oct 12, 2015 at 22:26
  • Was heading home. Yes, each one takes 1 more argument than the name suggests. It randoms the X answers and has 1 more input to choose one
    – Jason
    Commented Oct 12, 2015 at 23:50
  • And I'm an idiot, now I see why you asked, my meta code was off by 1 on each, it should say "if length = 6, RandFiveQ" and so on, sorry for that!
    – Jason
    Commented Oct 13, 2015 at 0:20

2 Answers 2

4

You can define four helper macros:

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\RandQ}{m}
 {
  \int_case:nnF { \clist_count:n { #1 } }
   {
    {2}{\RandTwoQL{#1}}
    {3}{\RandThreeQL{#1}}
    {4}{\RandFourQL{#1}}
    {5}{\RandFiveQL{#1}}
   }
   {Wrong~number~of~args~(#1)}
 }
\NewDocumentCommand{\RandTwoQL}{>{\SplitArgument{1}{,}}m}
 {
  \RandTwoQ#1
 }
\NewDocumentCommand{\RandThreeQL}{>{\SplitArgument{2}{,}}m}
 {
  \RandThreeQ#1
 }
\NewDocumentCommand{\RandFourQL}{>{\SplitArgument{3}{,}}m}
 {
  \RandFourQ#1
 }
\NewDocumentCommand{\RandFiveQL}{>{\SplitArgument{4}{,}}m}
 {
  \RandFiveQ#1
 }
\ExplSyntaxOff

\newcommand{\RandTwoQ}[2]{-#1-#2-}
\newcommand{\RandThreeQ}[3]{-#1-#2-#3-}
\newcommand{\RandFourQ}[4]{-#1-#2-#3-#4-}
\newcommand{\RandFiveQ}[5]{-#1-#2-#3-#4-#5-}

\begin{document}

\RandQ{1,2}

\RandQ{1,2,3}

\RandQ{1,2,3,4}

\RandQ{1,2,3,4,5}

\end{document}

The main macro computes the number of items and calls the appropriate macro that, in turn, splits the argument into the correct number of braced arguments through \SplitArgument.

I've used a fake definition of your four macros. Something better could be done if the definitions are known.

enter image description here

2
  • This looks like a pretty viable option. I'd like all this to be in a STY file to simplify my life in the individual files... can all the commands here be put into a STY file without trouble?
    – Jason
    Commented Oct 12, 2015 at 21:58
  • @Jason You can (almost) always put code into a style file with no issue. If not, then you probably know enough TeX to know the difference by sheer virtue of needing such a thing :-) Commented Oct 13, 2015 at 2:54
1

Here's a LuaLaTeX-based solution. It sets up a Lua function which (a) scans all input lines at a very early stage of processing and (b) replaces all instances of \RandQ{...} selectively with \Rand<num>Q, where <num> depends on the number of comma-separated items in the argument of \RandQ.

The following example provides both the Lua code as well as some dummy definitions for \RandFiveQ, \RandFourQ, etc. in order to create a standalone working example

enter image description here

% !TEX TS-program = lualatex
\documentclass{article}

%% Lua-side code: Define the function "do_RandQ" and assign
%% it to the "process_input_buffer" callback
\usepackage{luacode}
\begin{luacode}
function do_RandQ ( line )
  line = string.gsub ( line , "Q{(.-),(.-),(.-),(.-),(.-)}", "FiveQ{%1}{%2}{%3}{%4}{%5}" )
  line = string.gsub ( line , "Q{(.-),(.-),(.-),(.-)}", "FourQ{%1}{%2}{%3}{%4}" )
  line = string.gsub ( line , "Q{(.-),(.-),(.-)}", "ThreeQ{%1}{%2}{%3}" )
  line = string.gsub ( line , "Q{(.-),(.-)}", "TwoQ{%1}{%2}" )
  return ( line )
end
luatexbase.add_to_callback ( "process_input_buffer" , 
    do_RandQ, "do_RandQ" )
\end{luacode}

%% TeX-side code: Dummy definitions of \Rand<num>Q macros
\def\RandFiveQ#1#2#3#4#5{#1---#2---#3---#4---#5}
\def\RandFourQ#1#2#3#4{#1?#2?#3?#4}
\def\RandThreeQ#1#2#3{#1\dots#2\dots#3}
\def\RandTwoQ#1#2{#1??#2}

\begin{document}    
%% Four instances of "\RandQ", each one with a different number of items
\RandQ{1,2,3,4,5}

\RandQ{a,b,c,d}

\RandQ{A,B,C}

\RandQ{x,y}
\end{document}

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .