How to pass multiple random numbers (generated using lcg package) to a macro?
As said by @StevenB.Segletes, \rand
isn't expandable, therefore one can't record the produced random numbers in a macro simply using this function. For the same reason, even if \showsum
were trying to recursively expand your \random
, it wouldn't be able to get anything useful from it—and this wouldn't be better if you passed it directly the argument \rand\arabic{rand} \rand\arabic{rand} \rand\arabic{rand}
, which is only one expansion step away from your \random
.
In order to solve this, I propose to use the \int_rand:nn
function from expl3
, which does produce random integers in an expandable manner (the bounds provided as arguments are both included in the possible results). Moreover, I'll wrap this up so that for instance, calling \myrandsums{5}{1000}{9999}
prints a sum of five random integers n for which 1000 ≤ n ≤ 9999.
\documentclass{article}
% 'geometry' is only used so that the examples nicely fit on a single line.
\usepackage[hmargin=2cm]{geometry}
\usepackage{stringstrings}
\usepackage{stackengine}
\usepackage{xparse}
\ExplSyntaxOn
\cs_new_protected:Npn \latexfan_showsum:n #1
{
\showsum {#1}
}
\cs_generate_variant:Nn \latexfan_showsum:n { x }
\cs_generate_variant:Nn \seq_use:Nn { NV }
\NewDocumentCommand \myrandsums { m m m }
{
\seq_clear:N \l_tmpa_seq
\int_step_inline:nn {#1}
{ \seq_put_right:Nx \l_tmpa_seq { \int_rand:nn {#2} {#3} } }
\latexfan_showsum:x { \seq_use:NV \l_tmpa_seq \c_space_tl }
}
\ExplSyntaxOff
% Equivalent to the desired \random macro from your example
\newcommand{\hardcodedSumOfThree}{\myrandsums{3}{0}{1000}}
\newcounter{mysum}
\newcommand\showsum[1]{%
\convertchar[q]{#1}{ }{+}%
\setcounter{mysum}{\numexpr\thestring\relax}%
\def\stackalignment{r}%
\if T\showsums\edef\tmp{\themysum}\else\edef\tmp{~}\fi%
\raisebox{-\dp\strutbox}{+\,}{\stackunder{\underline{\ \Longstack{#1}}}{%
\tmp}}%
}
\begin{document}
\def\showsums{T}% Print the result
\showsum{411 319 217}%
%
\qquad
\myrandsums{3}{0}{1000}%
%
\qquad
\hardcodedSumOfThree % ditto
%
\qquad
\myrandsums{5}{1000}{9999}%
%
\qquad
\def\showsums{F}% Don't print the result
\myrandsums{8}{1000}{9999}%
\end{document}
Sample output:
The following is a slightly more elaborate variant. It provides a \myrandsums
function that behaves the same as above, and additionally \myRandsums
that ignores the current contents of \showsums
:
\myRandsums*{<num>}{<min>}{<max>}
always prints the sum of the randomly chosen integers;\myRandsums{<num>}{<min>}{<max>}
never prints the result, only the operands.
\documentclass{article}
% 'geometry' is only used so that the first series of examples nicely fits on a
% single line.
\usepackage[hmargin=2cm]{geometry}
\usepackage{stringstrings}
\usepackage{stackengine}
\usepackage{xparse}
\ExplSyntaxOn
\cs_new_protected:Npn \latexfan_showsum:n #1
{
\showsum {#1}
}
\cs_generate_variant:Nn \latexfan_showsum:n { x }
\cs_generate_variant:Nn \seq_use:Nn { NV }
\cs_new_protected:Npn \latexfan_randsums:nnn #1#2#3
{
\seq_clear:N \l_tmpa_seq
\int_step_inline:nn {#1}
{ \seq_put_right:Nx \l_tmpa_seq { \int_rand:nn {#2} {#3} } }
\latexfan_showsum:x { \seq_use:NV \l_tmpa_seq \c_space_tl }
}
\NewDocumentCommand \myrandsums { m m m }
{
\latexfan_randsums:nnn {#1} {#2} {#3}
}
\NewDocumentCommand \myRandsums { s m m m }
{
\group_begin:
\cs_set:Npx \showsums { \IfBooleanTF {#1} {T} {F} }
\latexfan_randsums:nnn {#2} {#3} {#4}
\group_end:
}
\ExplSyntaxOff
% Equivalent to the desired \random macro from your example
\newcommand{\hardcodedSumOfThree}{\myrandsums{3}{0}{1000}}
\newcounter{mysum}
\newcommand\showsum[1]{%
\convertchar[q]{#1}{ }{+}%
\setcounter{mysum}{\numexpr\thestring\relax}%
\def\stackalignment{r}%
\if T\showsums\edef\tmp{\themysum}\else\edef\tmp{~}\fi%
\raisebox{-\dp\strutbox}{+\,}{\stackunder{\underline{\ \Longstack{#1}}}{%
\tmp}}%
}
\begin{document}
\def\showsums{T}%
\showsum{411 319 217}% Print the result
%
\qquad
\myrandsums{3}{0}{1000}%
%
\qquad
\hardcodedSumOfThree % ditto
%
\qquad
\myrandsums{5}{1000}{9999}%
%
\qquad
\def\showsums{F}% Don't print the result
\myrandsums{8}{1000}{9999}%
\bigskip
\qquad
\myRandsums*{3}{100}{999}%
\qquad
\myRandsums{2}{10}{99}%
\qquad
\myRandsums*{2}{10}{99}%
\qquad
\myRandsums{3}{100}{999}%
\end{document}
Sample output:
An implementation with expl3
a bit different from frougon's, not needing extra packages.
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\randomsum}{smmm}
{% #1 = * to show, #2 = summands, #3 = lower bound, #4 = upper bound
\IfBooleanTF{#1}
{
\bool_set_false:N \l__latexfan_randomsum_solution_bool
}
{
\bool_set_true:N \l__latexfan_randomsum_solution_bool
}
\latexfan_randomsum:nnn { #2 } { #3 } { #4 }
}
\bool_new:N \l__latexfan_randomsum_solution_bool
\seq_new:N \l__latexfan_randomsum_summands_seq
\cs_new_protected:Nn \latexfan_randomsum:nnn
{
\seq_clear:N \l__latexfan_randomsum_summands_seq
% make a sequence with random numbers
\int_step_inline:nn { #1 }
{
\seq_put_right:Nx \l__latexfan_randomsum_summands_seq { \int_rand:nn { #2 } { #3 } }
}
% print the summands, first a raised +
\raisebox{0.51\normalbaselineskip}{$+$}\,
% the summands in column, with a rule in the middle
\begin{tabular}[b]{@{\,}r@{}}
\seq_use:Nn \l__latexfan_randomsum_summands_seq { \\ } \\
\hline
\bool_if:NTF \l__latexfan_randomsum_solution_bool
{% a phantom of the sum to hint at the number of digits
\phantom { \int_eval:n { \seq_use:Nn \l__latexfan_randomsum_summands_seq { + } } }
}
{% the sum
\int_eval:n { \seq_use:Nn \l__latexfan_randomsum_summands_seq { + } }
}
\end{tabular}
}
\ExplSyntaxOff
\begin{document}
\randomsum*{2}{10}{90}\qquad
\randomsum{4}{100}{999}\qquad
\randomsum{7}{1000}{9999}\qquad
\randomsum*{7}{1000}{9999}
\end{document}