8

I often need to place random elements within a document in ConTeXt. For example, I added some random language variances to a form letter.

In each instance, there is a choice of items to be displayed:

Print
   a
Or
   b
Or
   c
Done

In some cases, I need to nest the randomness:

Print
   a
Or
   Print
       b
   Or
       c
   Done
Or
   d
Done

I created this code which achieves what I need, but the syntax is not very efficient to work with:

\starttext

    \define[2]\loadrandom{%
        \getrandomnumber \randomnumber {#1} {#2}%
    }

    \loadrandom{1}{10}%
    %
    \ifnum\randomnumber=1%
        This will print the first item.
    \fi%
    %
    \ifnum\randomnumber=2%
        This will print the second item.
    \fi%
    %
    \ifnum\randomnumber=3%
        This will print the third item.
    \fi%
    %
    \ifnum\randomnumber=4%
        This will print the fourth item.
    \fi%
    %
    \ifnum\randomnumber=5%
        This will print the fifth item.
    \fi%
    %
    \ifnum\randomnumber=6%
        This will print the sixth item.
    \fi%
    %
    \ifnum\randomnumber=7%
        This will print the seventh item.
    \fi%
    %
    \ifnum\randomnumber=8%
        This will print the eigth item.
    \fi%
    %
    \ifnum\randomnumber=9%
        This will print the ninth item.
    \fi%
    %
    \ifnum\randomnumber=10%
        This will print the tenth item.
    \fi%

\stoptext

Is there any way to improve on this code so it is easier to quickly place random text in the page?

  • 3
    Have a look at \ifcase. This will abbreviate your code a lot. – Henri Menke Aug 2 '14 at 23:32
  • Or with expl3 the command \int_case:Nn(TF). – Manuel Aug 3 '14 at 23:37
  • Interesting. Good question. But it's not random. You still have to change the value manually, on the second argument of \loadrandom – doed Aug 4 '14 at 4:03
  • @doed It's random, you only select the number of options (e.g., the number of “cases” you offer inside the \ifcase), and then it selects it for you. It's possible, in case you want to automatise that, to let the command count the total number of options and then get a random number between 0 and that number. – Manuel Aug 4 '14 at 10:04
  • @Manuel hehe, right. I realized that afterwards. I had come across that other question in LaTeX, and I assumed that that was, what Village wanted. As @HenriMenke pointed out, \ifcase resolves the issue. But, I still have a problem with the word random, aside from any philosophical discourse. Or should I start playing the lottery jackpot all of a sudden? – doed Aug 4 '14 at 10:19
5
+250

May be you need this. My example included nested random printing, if the first random number is equal to 2.

\getrandomnumber \randomnumber {1} {10}
\ifcase\randomnumber \or
   print a
   \or
   \getrandomnumber \randomnumber {1} {2}
   \ifcase\randomnumber \or
       print ba
       \or
       print bb
   \fi
   \or
   print c
   \or
   print d
   \or
   print e
   \or
   print f
   \or
   print g
   \or
   print h
   \or
   print i
   \or
   print j
\fi

Or you can define a macro \randomtext:

\newcount\tmpnum
\def\randomtext#1{\def\tmpbuf{}\tmpnum=0\randomtextA#1;;}
\def\randomtextA#1;{\if;#1;\getrandomnumber\randomnumber{1}{\tmpnum}%
      \expandafter\ifcase\expandafter\randomnumber\tmpbuf\fi
   \else \expandafter\def\expandafter\tmpbuf\expandafter{\tmpbuf\or#1}%
      \advance\tmpnum by1
      \expandafter\randomtextA
   \fi
}

and use it:

\randomtext{a;b;c;d;e;f}
or nested:
\randomtext{a;\randomtext{ba;bb};c;d;e}

Sorry, I didn't test it because I don't use ConTeXt. But I hope that it will work... Because ConTeXt is based on TeX.

Edit: Because ConTeXt uses pseudorandom generator, you need to call the \randomtext more than once in order to see the effect:

\newcount\tmpnum
\def\randomtext#1{\def\tmpbuf{}\tmpnum=0\randomtextA#1;;}
\def\randomtextA#1;{\if;#1;\getrandomnumber\randomnumber{1}{\tmpnum}%
      \expandafter\ifcase\expandafter\randomnumber\tmpbuf\fi
   \else \expandafter\def\expandafter\tmpbuf\expandafter{\tmpbuf\or#1}%
      \advance\tmpnum by1
      \expandafter\randomtextA
   \fi
}

\starttext

\randomtext{a;\randomtext{(ba);(bb)};c;d;e}
\randomtext{a;\randomtext{(ba);(bb)};c;d;e}
\randomtext{a;\randomtext{(ba);(bb)};c;d;e}
\randomtext{a;\randomtext{(ba);(bb)};c;d;e}
\randomtext{a;\randomtext{(ba);(bb)};c;d;e}
\randomtext{a;\randomtext{(ba);(bb)};c;d;e}
\randomtext{a;\randomtext{(ba);(bb)};c;d;e}
\randomtext{a;\randomtext{(ba);(bb)};c;d;e}
\randomtext{a;\randomtext{(ba);(bb)};c;d;e}
\randomtext{a;\randomtext{(ba);(bb)};c;d;e}
\randomtext{a;\randomtext{(ba);(bb)};c;d;e}
\randomtext{a;\randomtext{(ba);(bb)};c;d;e}
\randomtext{a;\randomtext{(ba);(bb)};c;d;e}
\randomtext{a;\randomtext{(ba);(bb)};c;d;e}
\randomtext{a;\randomtext{(ba);(bb)};c;d;e}
\randomtext{a;\randomtext{(ba);(bb)};c;d;e}

\stoptext
10

There are many ways to set the texts for the random value, the first is the low level \ifacse macro.

\define\RandomText
  {\getrandomnumber\randomnumber{1}{4}%
   \ifcase\randomnumber
     % 0
   \or
     First item.% 1
   \or
     Second item.% 2
   \or
     Third item.% 3
   \else
     Another item.% 4+
   \fi}

\starttext
\dorecurse{3}{\RandomText\par}
\stoptext

The ConTeXt equivalent to this is the \processaction command. The first argument of the command takes the argument which should be searched in the list and the second argument takes values separated by the text with a =>. You can also set a text which should be used when the searched value isn’t in the list with the unknown key.

\define\RandomText
  {\getrandomnumber\randomnumber{1}{4}%
   \processaction
     [\randomnumber]
     [      1=>First item.,
            2=>Second item.,
            3=>Third item.,
      unknown=>Another item.]}

\starttext
\dorecurse{3}{\RandomText\par}
\stoptext

When the text you want show is only I suggest a different method where it is easier to write the text. There are two ways possible which are very similar, the first uses the setups environment (I used rawsetups because it keeps the space at the end of each line in the input) and the second the buffer environment.

When your text don’t contains anything special like verbatim etc. setupscan be used without problems.

\startrawsetups[randomtext:1]
  First item.
\stoprawsetups

\startrawsetups[randomtext:2]
  Second item.
\stoprawsetups

\startrawsetups[randomtext:3]
  Third item.
\stoprawsetups

\startrawsetups[randomtext:unknown]
  Another item.
\stoprawsetups

\define\RandomText
  {\getrandomnumber\randomnumber{1}{4}%
   \doifsetupselse{randomtext:\randomnumber}
     {\directsetup{randomtext:\randomnumber}}
     {\directsetup{randomtext:unknown}}}

\starttext
\dorecurse{3}{\RandomText\par}
\stoptext

When you have also verbatim texts or other special things for TeX buffers are the better solution because the don’t have problems with them.

\startbuffer[randomtext:1]
  First item.
\stopbuffer

\startbuffer[randomtext:2]
  Second item.
\stopbuffer

\startbuffer[randomtext:3]
  Third item.
\stopbuffer

\startbuffer[randomtext:unknown]
  Another item.
\stopbuffer

\define\RandomText
  {\getrandomnumber\randomnumber{1}{4}%
   \doifelsebuffer{randomtext:\randomnumber}
     {\getbuffer[randomtext:\randomnumber]}
     {\getbuffer[randomtext:unknown]}}

\starttext
\dorecurse{3}{\RandomText\par}
\stoptext
2

This is a snippet programmed in Lua run from LuaLaTeX, it can be run in ConTeXt easily after minor modification of the TeX structure. We get a number of elements in the table, then we randomly pick up one item and decide if it is a table type. If it is, we call that function again one level deeper, otherwise we print the selected item.

In this example we run that function ten times to check if it is working as expected.

% run: lualatex mal-random.tex
\documentclass{article}
\usepackage{luacode}

\begin{luacode*}
-- Some input data...
math.randomseed(0)
randomdata={
  "first",
  {"second-a", "second-b", "second-c"},
  "third",
  "fourth",
  "fifth",
  }
-- Repetition of the function...
function randomme (number)
for i=1,number do -- repetition
  randomcall(randomdata)
end --for i
end --function randomme

function randomcall (input)
randompick=math.random(#input)
randomcell=input[randompick]
print(#input, randompick, randomcell)
if type(randomcell)=="table" then  
  randomcall(randomcell)
    else
  print("Selected item ("..randompick..".): "..randomcell)
  tex.print(randomcell)
end -- if type
end --function randomcall
\end{luacode*}

\begin{document}
\directlua{randomme(10)}
\end{document}

After runnning lualatex mal-random.tex we get these messages in the terminal (it lists the size of the actual level, the order of the selected item and the item itself):

5   2   table: 01524790
3   2   second-b
Selected item (2.): second-b
5   1   first
Selected item (1.): first
5   2   table: 01524790
3   2   second-b
Selected item (2.): second-b
5   2   table: 01524790
3   3   second-c
Selected item (3.): second-c
5   2   table: 01524790
3   3   second-c
Selected item (3.): second-c
5   1   first
Selected item (1.): first
5   5   fifth
Selected item (5.): fifth
5   1   first
Selected item (1.): first
5   1   first
Selected item (1.): first
5   4   fourth
Selected item (4.): fourth

We shall see the selected non-table items in the PDF file: second-b first second-b second-c second-c first fifth first first fourth.

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.