6

I have a command whose definition is given randomly. Every time I call it, it's redefined again, randomly. I'd like for it to be redefined only when I want it.

The MWE is the following. Notice that the part between \ExplSyntaxOn and \ExplSyntaxOff is needed to take random elements from a given list. It defines the \selectNrandom and \htguse commands, used in the \name command. I found it in this other thread: Selecting random elements from a comma separated list.

\documentclass[11pt, a4paper]{article}

\usepackage[italian]{babel}
\usepackage[utf8]{inputenc}
\usepackage{ifthen}
\usepackage{calc}
\usepackage{pgffor}
\usepackage{xparse}
\usepackage{lcg}

\input{random}

\newcommand{\name}{\selectNrandom{1}{Francesco, Alessandro, Mattia, Lorenzo, Leonardo, Andrea, Gabriele, Matteo, Tommaso, Riccardo, Davide, Giuseppe, Edoardo, Antonio, Federico, Giovanni, Marco, Diego, Samuele, Pietro, Christian, Nicolò, Luca, Simone, Filippo, Alessio, Gabriele, Michele, Emanuele, Jacopo}\htguse{1}}

% The following part, up to \ExplSyntaxOff, is needed to take random elements from a given list. It defines the \selectNrandom and \htguse commands, used in the \name command. I found it in https://tex.stackexchange.com/questions/86165/selecting-random-elements-from-a-comma-separated-list/86168#86168

\ExplSyntaxOn
\NewDocumentCommand{\htguse}{ m }
{
    \use:c { htg_arg_#1: }
}
\NewDocumentCommand{\selectNrandom}{ m m m }
{
    \htg_select_n_random:nnn { #1 } { #2 } { #3 }
}

\cs_new_protected:Npn \htg_select_n_random:nnn #1 #2 #3
{
    \seq_clear:N \l_htg_used_seq
    \int_set:Nn \l_htg_length_int { \clist_count:n { #2 } }
    \int_compare:nTF { #1 > \l_htg_length_int }
    {
        \msg_error:nnxx { randomchoice } { too-many } { #1 } { \int_to_arabic:n { \l_htg_length_int } }
    }
    {
        \int_step_inline:nnnn { 1 } { 1 } { #1 }
        {
            \htg_get_random:
            \cs_set:cpx { htg_arg_##1: }
            { \clist_item:nn { #2 } { \l_htg_random_int } }
        }
        #3
    }
}
\cs_new_protected:Npn \htg_get_random:
{
    \setrannum { \l_htg_random_int } { 1 } { \l_htg_length_int }
    \seq_if_in:NxTF \l_htg_used_seq { \int_to_arabic:n { \l_htg_random_int } }
    { \htg_get_random: }
    { \seq_put_right:Nx \l_htg_used_seq { \int_to_arabic:n { \l_htg_random_int } } }
}
\seq_new:N \l_htg_used_seq
\int_new:N \l_htg_length_int
\int_new:N \l_htg_random_int
\msg_new:nnnn { randomchoice } { too-many }
{ Too~ many~choices }
{ You~want~to~select~#1~elements,~but~you~have~only~#2 }
\ExplSyntaxOff

\begin{document}

\foreach \pagenum in {1,...,5}{

    \name

    \name

    \name

\pagebreak

}

\end{document}

Now, the job of this code is to take a random name from that list. It takes a new, random name every time. Let's say that I want it to take a new name only when it creates a new page - so that every one of those 5 pages will have the same name displayed three times on them, every page with a different name (unless the random function gives the exact same result). How should I proceed?

  • This is much easier nowadays with \seq_rand_item:N but the expandability of the command will pose a problem... with your setup – user31729 Mar 7 '17 at 14:20
5

In plain TeX, you can use \input random. Define \preprandom for changing the result and \result prints the randomly chosen result:

\input random

\newcount\rann
\newcount\tmpnum

\def\preprandom{\setrannum\rann{1}{30}}
\def\result{\ifcase\rann\or
  Francesco\or Alessandro\or Mattia\or Lorenzo\or Leonardo\or
  Andrea\or Gabriele\or Matteo\or Tommaso\or Riccardo\or Davide\or
  Giuseppe\or Edoardo\or Antonio\or Federico\or Giovanni\or Marco\or
  Diego\or Samuele\or Pietro\or Christian\or Nicolò\or Luca\or Simone\or 
  Filippo\or Alessio\or Gabriele\or Michele\or Emanuele\or Jacopo\fi
}

\tmpnum=0
\loop
   \preprandom 
   \result\endgraf \result\endgraf \result\endgraf \medskip\hrule\medskip
   \advance\tmpnum by1
   \ifnum\tmpnum<5 \repeat

\bye
  • this works perfectly :) – il mietitore Mar 10 '17 at 20:20
  • I can't find informations about this \input random thing. I'd like to give it a seed or something, but I can't understand how to do this. Where can I read more about it? – il mietitore Mar 11 '17 at 13:42
  • The random.tex macro file is documented at the beginning of this file using comments. – wipet Mar 13 '17 at 9:05
4

How about pre-storing the full list of names in a \seq variable first and use \seq_rand_item:N which is a 'new' feature (introduced on 2016/12/06) and is expandable, i.e. its expansion value can be stored to some other macro, say \edef\namestored{\name}

\documentclass[11pt, a4paper]{article}

\usepackage[italian]{babel}
\usepackage[utf8]{inputenc}
\usepackage{pgffor}
\usepackage{xparse}



\ExplSyntaxOn

\seq_new:N \l_candidates_name_seq

\NewDocumentCommand{\StoreNames}{m}{%
  \seq_set_from_clist:Nn \l_candidates_name_seq {#1}
}

\cs_new:Npn \selectNrandom #1 {%
  \seq_rand_item:N \l_candidates_name_seq 
}

\newcommand{\name}{\selectNrandom{1}}

\ExplSyntaxOff

\StoreNames{Francesco, Alessandro, Mattia, Lorenzo, Leonardo, Andrea, Gabriele, Matteo, Tommaso, Riccardo, Davide, Giuseppe, Edoardo, Antonio, Federico, Giovanni, Marco, Diego, Samuele, Pietro, Christian, Nicolò, Luca, Simone, Filippo, Alessio, Gabriele, Michele, Emanuele, Jacopo}



\begin{document}


\foreach \pagenum in {1,...,5}{
  \edef\namestored{\name}

  \namestored

  \namestored

  \namestored
  \medskip
  \hrule
  \medskip
   %\pagebreak

}

\end{document}

Due to its random nature the output of the run will change (unless the random seed is fixed)

enter image description here

Storing with \cs_new:Npx \namestored:

\documentclass[11pt, a4paper]{article}

\usepackage[T1]{fontenc}
\usepackage[utf8]{inputenc}
\usepackage[italian]{babel}
\usepackage{pgffor}
\usepackage{xparse}



\ExplSyntaxOn

\seq_new:N \l_candidates_name_seq

\NewDocumentCommand{\StoreNames}{m}{%
  \seq_set_from_clist:Nn \l_candidates_name_seq {#1}
}

\cs_new:Npn \selectNrandom #1 {%
  \seq_rand_item:N \l_candidates_name_seq 
}

\cs_new:Npn \namex {
  \cs_set:Npx \namestored {\seq_rand_item:N \l_candidates_name_seq }
}


\newcommand{\name}{\selectNrandom{1}}

\ExplSyntaxOff

\StoreNames{Francesco, Alessandro, Mattia, Lorenzo, Leonardo, Andrea, Gabriele, Matteo, Tommaso, Riccardo, Davide, Giuseppe, Edoardo, Antonio, Federico, Giovanni, Marco, Diego, Samuele, Pietro, Christian, Nicolò, Luca, Simone, Filippo, Alessio, Gabriele, Michele, Emanuele, Jacopo}



\begin{document}


\foreach \pagenum in {1,...,5}{
  \namex

  \namestored

  \namestored

  \namestored
  \medskip
  \hrule
  \medskip

}

\end{document}
  • this looks interisting but I can't understand how to use this. I updated my TeXlive to 2016 version, and still if I copy-paste any of these two codes they won't work :\ – il mietitore Mar 10 '17 at 17:39
  • @ilmietitore: Well, do you think the code in your original post was easier? You have to use the latest updates. And the screen shot proves that it works, at least under TL 2016 on Linux – user31729 Mar 10 '17 at 18:53
  • it's not like I don't believe you, it's just that I don't get why it doesn't work. I updated it and in fact I see from the terminal that my version is TeX Live 2016/Debian. Does this version of TeXlive include all updates until the very end of 2016 - so, also the one you mentioned? – il mietitore Mar 10 '17 at 20:19
  • @ilmietitore: The Debian packages have a time lag in updates. I dropped the usage of the packages provided by the Linux Distributions about 5 years ago and always installed from the net TL archive. Most likely, the December additions of xparse did not find its way into the debian packages yet. – user31729 Mar 10 '17 at 20:23
  • I used a PPA, but I guess this must me the reason. Thank you for the advice concerning the TL archive :) tomorrow I'll work on that and see if I can make it work – il mietitore Mar 10 '17 at 20:26

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.