Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

I try to find a way to make a

\newcommand

so that it has different outputs, that it chooses randomly. In the best case the macro should also avoid word repetitions (something like, only choose this word if it hasnt been used for 50 words).

In this macro I want to group similar words, I use a lot in the document. For example if I often write the same name, it should sometimes just print the family name and other times the full name.

Edit another example: the macro should print "the group" or "the group of people" or any different synonym, so that I just have one macro for this pool of words.

share|improve this question
    
Interesting question ;-) –  Christian Hupfer Aug 21 at 10:16
    
What do you want to achieve with it? What is the context? If you just need some text lipsum can help. Some "rubbish" can be generated for example here. –  Hackbard_C Aug 21 at 10:17
    
@Hackbard_C: Did you read the question??? –  Christian Hupfer Aug 21 at 10:17
1  
varioref does something similar (but just alternates between two styles) –  David Carlisle Aug 21 at 10:19
2  
@Benutzer193 Thanks for the explanation. Can this random quote selector help? –  Hackbard_C Aug 21 at 10:25

3 Answers 3

up vote 13 down vote accepted

You can define the macro by specifying a relative frequency:

\documentclass{article}
\usepackage{xparse}
\input{random}

\ExplSyntaxOn
% alias \setrannum to an expl3 function
\cs_set_eq:NN \benutzer_set_rannum:Nnn \setrannum

\NewDocumentCommand{\newrandomcommand}{mm}
 {% #1 is the macro name, #2 is the data
  \cs_if_exist:cTF { #1 }
   {
    ERROR!
   }
   {
    \benutzer_new_random_command:nn { #1 } { #2 }
   }
 }

\seq_new:N \l__benutzer_data_seq
\int_new:N \l__benutzer_random_int

\cs_new_protected:Npn \benutzer_new_random_command:nn #1 #2
 {
  \seq_set_split:Nnn \l__benutzer_data_seq { , } { #2 }
  \seq_new:c { g_benutzer_#1_data_seq }
  \benutzer_process_data:n { #1 }
  \cs_new_protected:cpn { #1 }
   {
    \benutzer_set_rannum:Nnn \l__benutzer_random_int { 1 }
     { \seq_count:c { g_benutzer_#1_data_seq } }
    \seq_item:cn { g_benutzer_#1_data_seq } { \l__benutzer_random_int }
   }
 }

\cs_new_protected:Npn \benutzer_process_data:n #1
 {
  \seq_map_inline:Nn \l__benutzer_data_seq
   {
    \__benutzer_process_item:nw { #1 } ##1 // \q_stop
   }
 }

\cs_new:Npn \__benutzer_process_item:nw #1 #2 / #3 / #4 \q_stop
 {
  \tl_if_blank:nTF { #3 }
   {
    \__benutzer_add_item:nnn { #1 } { #2 } { 1 }
   }
   {
    \__benutzer_add_item:nnn { #1 } { #2 } { #3 }
   }
 }

\cs_new_protected:Npn \__benutzer_add_item:nnn #1 #2 #3
 {
  \prg_replicate:nn { #3 }
   {
    \seq_put_right:cn { g_benutzer_#1_data_seq } { #2 }
   }
 }

% this is just for testing
\NewDocumentCommand{\test}{mm}
 {
  \prg_replicate:nn { #2 } { #1\space }
 }

\ExplSyntaxOff

% Fyodor will appear 10/61 times on average
% Michailovich will appear 1/61 times on average
% Dostoyevsky will appear 50/61 times on average
\newrandomcommand{fyodor}{Fyodor/10, Mikhailovich, Dostoyevsky/50}

\begin{document}

\raggedright
\test{\fyodor}{100}

\end{document}

enter image description here

share|improve this answer
    
This solution is really good. I specially like the option to set the average appearance of an word. Thanks for the answer. –  Benutzer193 Aug 21 at 12:18

This is a preliminary version. The accounting of how much a word has been called so far is not done so far. I will update the solution...

Just define a word pool list with \NewWordPool{poolname}{WordA,WordB,{SentenceA}}

and say \pickuprandomword{\poolname}

The pool name is just like a macro name, but without \ character

\documentclass{article}
\usepackage{datetime}%
\usepackage{tikz}%
\usepackage{ifthen}%
\usepackage{etoolbox}%

\listgadd{\GroupWordsPools}{}%

\listgadd{\GroupWordsPoolsCount}{}%

\usepackage{forloop}%

\newcommand{\NewWordPool}[2]{%
\csxdef{#1}{}%
\forcsvlist{\listcsxadd{#1}}{#2}%
\csxdef{#1values}{}%
\forcsvlist{\listcsxadd{#1values}}{0}%
}%


\newcounter{loopcounter}%

\newcounter{randompos}%

\def\lastrandom{0}%

\newcommand{\pickuprandomword}[2][50]{%
\def\currentrandom{}%
%First counter the number of elements in the list
\renewcommand*{\do}[1]{%
\stepcounter{randompos}%
}{}%
\dolistloop{#2}% 
\pgfmathrandominteger{\currentrandom}{1}{\number\value{randompos}}%
\whiledo{\lastrandom =\currentrandom}{%
\pgfmathrandominteger{\currentrandom}{1}{\number\value{randompos}}%
}%
\setcounter{randompos}{0}%
\renewcommand*{\do}[1]{%
\stepcounter{randompos}%
\ifnumequal{\number\value{randompos}}{\currentrandom}{%
% Output the name
##1%
\listbreak%
}{}%
}%
\dolistloop{#2}%
\edef\lastrandom{\currentrandom}%
}%



\begin{document}

\NewWordPool{GroupWordsPool}{Hamlet,{Hamlet -- Prince of Denmark}, Shakespeare,{William Shakespeare}, Einstein,{Albert Einstein},Frodo, Bilbo, Gandalf,Aragorn,Galadriel,{Thorin Oakshield},Pippin,Elrond,Heisenberg,StackExchange}%


\forloop{loopcounter}{1}{\value{loopcounter} < 30}{%
\pickuprandomword{\GroupWordsPool}\par

}%

\end{document}

Edit I am still working on a better solution, but it needs time.

share|improve this answer
    
Also a really good answer, both ways do what I wanted to achieve! Thank you very much! –  Benutzer193 Aug 21 at 12:18
    
To the unknown downvoter: Leave a comment why you downvoted this answer –  Christian Hupfer Sep 4 at 16:43
    
I hope it wasn't me. Some time ago, I accidentely voted one answer down, because I was on my mobile, but I immediately changed the vote to the desired upvote. I can't remember which answer it was and on which question. –  Benutzer193 Sep 8 at 7:39
    
@Benutzer193: Nope, it was a serial downvoter... he has left TeX.SX already or was kicked ;-) –  Christian Hupfer Sep 8 at 7:41
    
That's good! :) –  Benutzer193 Sep 8 at 7:42

Update

Better Version for the lua file, with different probabilies

n = math.random(1,6) 
if n == 6 then tex.sprint("foo1")
elseif n >= 4 then tex.sprint("foo2") 
else tex.sprint("foo3")
end

(example probabilites:
1/6 => foo1
1/3 => foo2
1/2 => foo3
)

Another easier solution with the compromise of fewer options (and only if using Lualatex):

set a new command:

\newcommand{\test}{\directlua{  dofile("test.lua")  }\xspace}

test.lua:

local test = { 'a','b'}
tex.sprint(test[math.random( #test )])

a and b need to be changed the desired text output (more words can be added of course)
and test can be changed to the desired name of the command.


I don't know if or how I can restrict the word repetition like in the other solutions or how to set the average appearance of a word.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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