# Prune list for random order without repetition problem

I use prune list command from this tutorial Generating random numbers without repetitions for avoid repetitions while generate random sequence. But if I try to use this command in framed box, for example, it doesn't work. As well as in minipage environment. Why it happens?

\documentclass[a4paper,14pt]{extreport}
\usepackage{tikz}

\newcommand\lengthof[1]{\csname pgfmath@randomlist@#1\endcsname}
\newcommand\nthof[2]{\csname pgfmath@randomlist@#2@#1\endcsname}

\def\mylist{{one}{two}{three}{four}{five}}
\pgfmathdeclarerandomlist{mynum}{\mylist} %Define the list

\makeatletter
\def\prunelist#1{% Define prunelist command
\expandafter\edef\csname pgfmath@randomlist@#1\endcsname
{\the\numexpr\csname pgfmath@randomlist@#1\endcsname-1\relax}
\count@\pgfmath@randomtemp
\loop
\expandafter\let
\csname pgfmath@randomlist@#1@\the\count@\expandafter\endcsname
\csname pgfmath@randomlist@#1@\the\numexpr\count@+1\relax\endcsname
\ifnum\count@<\csname pgfmath@randomlist@#1\endcsname\relax
\repeat}
\makeatother

\newcommand\prnrnd{%
\pgfmathrandomitem{\mynum}{mynum}\mynum %get and print item from list
\prunelist{mynum} %prune list by selected item}

\begin{document}
\foreach \i in {1, ..., \lengthof{mynum}}%show list
{\par\textit{\nthof{\i}{mynum}}}\\

\fbox{\prnrnd}%extract item (item wasn't extracted!)
\foreach \i in {1, ..., \lengthof{mynum}}%show list
{\par\textit{\nthof{\i}{mynum}}}\\

\prnrnd%extract item
\foreach \i in {1, ..., \lengthof{mynum}}%show list
{\par\textit{\nthof{\i}{mynum}}}\\

\prnrnd%extract item
\foreach \i in {1, ..., \lengthof{mynum}}%show list
{\par\textit{\nthof{\i}{mynum}}}\\
\end{document}


You don't even need an \fbox to show this effect; enclosing \prnrnd in braces is sufficient. The explanation is that the changes by \prunelist are local to the current group. Outside of the group (the \fbox also forms a group) you are back to the old values, the effects of \prnrnd have been undone.

Here are two solutions; I recommend the first one.

# Change the list outside of the box

I recommend to modify your \prnrnd command such that it does not print the element but just stores it in a macro. Then you can execute it outside of the box and use the stored element inside the box. Replace \prnrnd by the following definition.

\newcommand\nextrnd[2]%
{\pgfmathrandomitem{\tmp}{#2}% select random element
\edef#1{\tmp}% store random element in #1
\prunelist{#2}%prune list by selected item
}


\nextrnd{<var>}{<list>} will pick and remove the next random element from <list> and store it in <var>.

To make your code easier to read, I also define a macro for listing the contents of the list.

\newcommand\showrndlist[1]%
{\foreach \i in {1, ..., \lengthof{#1}}
{\par\textit{\nthof{\i}{#1}}}%
\bigskip
}


Here is the complete code and its output.

\documentclass{article}
\usepackage{pgffor}

\newcommand\lengthof[1]{\csname pgfmath@randomlist@#1\endcsname}
\newcommand\nthof[2]{\csname pgfmath@randomlist@#2@#1\endcsname}

\newcommand\nextrnd[2]%
{\pgfmathrandomitem{\tmp}{#2}% select random element
\edef#1{\tmp}% store random element in #1
\prunelist{#2}%prune list by selected item
}
\newcommand\showrndlist[1]%
{\foreach \i in {1, ..., \lengthof{#1}}
{\par\textit{\nthof{\i}{#1}}}%
\bigskip
}

\makeatletter
\def\prunelist#1{% Define prunelist command
\expandafter\edef\csname pgfmath@randomlist@#1\endcsname
{\the\numexpr\csname pgfmath@randomlist@#1\endcsname-1\relax}
\count@\pgfmath@randomtemp
\loop
\expandafter\let
\csname pgfmath@randomlist@#1@\the\count@\expandafter\endcsname
\csname pgfmath@randomlist@#1@\the\numexpr\count@+1\relax\endcsname
\ifnum\count@<\csname pgfmath@randomlist@#1\endcsname\relax
\repeat}
\makeatother

\begin{document}
\def\mylist{{one}{two}{three}{four}{five}}
\pgfmathdeclarerandomlist{mynum}{\mylist}% Define the list

\showrndlist{mynum}

\nextrnd\rnd{mynum}% Store random element in \rnd and remove it from mynum
\fbox{\rnd}% The random element can be put inside a box
\showrndlist{mynum}

\nextrnd\rnd{mynum}% Store random element in \rnd and remove it from mynum
{\rnd}% ... or inside a group
\showrndlist{mynum}

\nextrnd\rnd{mynum}% Store random element in \rnd and remove it from mynum
\rnd
\showrndlist{mynum}
\end{document}


# Make the effects of \prunelist global

You can make the effect of \prunelist permanent beyond group ends by replacing

\expandafter\edef


by

\expandafter\xdef


and

\expandafter\let


by

\expandafter\global\expandafter\let


such that the definition becomes

\makeatletter
\def\prunelist#1{% Define prunelist command
\expandafter\xdef\csname pgfmath@randomlist@#1\endcsname % <<< \edef changed to \xdef
{\the\numexpr\csname pgfmath@randomlist@#1\endcsname-1\relax}
\count@\pgfmath@randomtemp
\loop
\csname pgfmath@randomlist@#1@\the\count@\expandafter\endcsname
\csname pgfmath@randomlist@#1@\the\numexpr\count@+1\relax\endcsname
\ifnum\count@<\csname pgfmath@randomlist@#1\endcsname\relax
\repeat}
\makeatother

• I'm beginner in LaTeX and still not quite understand the core mechanism. But I try :). Thanks alot for explanation. Jan 3 '17 at 19:15
• @Konstantin I have found a better solution; see my modified answer. Jan 4 '17 at 10:41
• Do you mean \expandafter\xdef instead of the more complicated \expandafter\global\expandafter\edef? Jan 4 '17 at 13:17
• I just want to write random print command with the list as argument. Thanks, first solution exactly what I need. Jan 4 '17 at 15:57