1

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
   \advance\count@\@ne
  \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}

enter image description here

1

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
   \advance\count@\@ne
  \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}

enter image description here enter image description here enter image description here enter image description here

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
   \expandafter\global\expandafter\let % <<< \expandafter\global added
   \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
   \advance\count@\@ne
  \repeat}
\makeatother
| improve this answer | |
  • I'm beginner in LaTeX and still not quite understand the core mechanism. But I try :). Thanks alot for explanation. – Konstantin Jan 3 '17 at 19:15
  • @Konstantin I have found a better solution; see my modified answer. – gernot Jan 4 '17 at 10:41
  • Do you mean \expandafter\xdef instead of the more complicated \expandafter\global\expandafter\edef? – Henri Menke 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. – Konstantin Jan 4 '17 at 15:57

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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