3

There are list of exam questions (as example 1-20). This list is divided on two parts (1-10, 11-20). Examination ticket contain two questions, first from first part and second from second part. This questions have to choose randomly without repetitions. Question random choose without repetition is solved before with \pgfmathdeclarerandomlist command.

But before examination tickets will generated questions list have to displayed in sequential order. So list were defined as:

\listadd{\mIqlist}{}%
\forcsvlist{\listadd\mIqlist}{{This, is},{is},{a},{very},{long},{text},{split},{in},{many}}

\listadd{\mIIqlist}{}%
\forcsvlist{\listadd\mIIqlist}{{than},{thirty},{items},{in},{here,}{but},{he},{did},{not}}

And for random purpose:

%вопросы первого блока
\newcounter{firsttotal}
\newcounter{firstcurr}
\setcounter{firsttotal}{9} %общее кол-во вопросов должно совпадать с кол-вом вопросов в списке
\newcommand\declarefirst{
\pgfmathdeclarerandomlist{firstlist}{\mIqlist}}
\declarefirst

%вопросы второго блока
\newcounter{secondtotal}
\newcounter{secondcurr}
\setcounter{secondtotal}{9} %общее кол-во вопросов должно совпадать с кол-вом вопросов в списке
\newcommand\declaresecond{
\pgfmathdeclarerandomlist{secondlist}{\mIIqlist}}
\declaresecond

%макрос \prunelist для удаления уже выбраных из списка вопросов для избежания повторений в первой итерации
\makeatletter
\def\prunelist#1{
\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\printfirst{
\pgfmathrandomitem{\z}{firstlist}
\z
\prunelist{firstlist}
\addtocounter{firstcurr}{1}
\ifnum \value{firstcurr} = \value{firsttotal} \setcounter{firstcurr}{0}\declarefirst \fi
}

%печатать вопрос из второго списка (выбирается случайным образом)
\newcommand\printsecond{
\pgfmathrandomitem{\z}{secondlist}
\z
\prunelist{secondlist}
\addtocounter{secondcurr}{1}
\ifnum \value{secondcurr} = \value{secondtotal} \setcounter{secondcurr}{0}\declaresecond \fi
}

In document sequential list displayed as:

\begin{enumerate}[label={\arabic*.}]
\renewcommand*{\do}[1]{\item {#1}}
\dolistloop{\mIqlist}
\dolistloop{\mIIqlist}
\end{enumerate} 

And in exam ticket:

\forloop{ct}{0}{\value{ct} < \value{TotalNum}}
{
 ....
 1.\printfirst \newline\newline
 2.\printsecond \newline
 ....
}

But while compile error occurs:

! Missing $ inserted.
<inserted text> 
                $
l.101 }
       %forloop

This error occure while I try \printfirst or \printsecond. As I understand this error is associated with list separator | symbol. How can I fix it?

1
  • I think this is something for expl3
    – user31729
    Dec 29, 2016 at 18:57

2 Answers 2

3

The easiest way to do arrays in LaTeX is to use \csname ...\endcsname. (You can also use the xstring package, but this is faster, albeit ugly.) The problem is that \csname has to be expanded before things like \let or \xdef, leading to lots of \expandafters. Also, \index0 is not a legal macro name and can only be implemented using \csname.

\documentclass{article}
\usepackage{tikz}

\newcommand{\mylist}{}% reserve global name

\begin{document}
\begin{tikzpicture}{overlay}% ignore spaces
\foreach \i in {0,...,5} {\expandafter\xdef\csname index\i\endcsname{\i}}% create indexes
\foreach \i in {0,...,5} {% randomise (last step redundant)
  \pgfmathparse{int(random(\i,5))}%
  \let\j=\pgfmathresult
  \expandafter\let\expandafter\tempa\csname index\i\endcsname
  \expandafter\let\expandafter\tempb\csname index\j\endcsname
  \global\expandafter\let\csname index\i\endcsname=\tempb
  \global\expandafter\let\csname index\j\endcsname=\tempa
}%
\xdef\mylist{\csname index0\endcsname}% create comma delimited list
\foreach \i in {1,...,5} {\xdef\mylist{\mylist,\csname index\i\endcsname}}%
\end{tikzpicture}

\noindent\mylist
\end{document}
1
  • 1
    should be noted, that int() is pretty crucial
    – dEmigOd
    Aug 4, 2021 at 7:12
3

Here is an implementation with the Knuth shuffle algorithm (https://tex.stackexchange.com/a/344488/4427)

\documentclass{article}
\usepackage[a6paper]{geometry}

\usepackage{xparse}

\ExplSyntaxOn

% user level commands

\NewDocumentCommand{\definefirstlist}{m}
 {
  \__konstantin_define_list:Nn \g_konstantin_firstlist_prop { #1 }
 }
\NewDocumentCommand{\definesecondlist}{m}
 {
  \__konstantin_define_list:Nn \g_konstantin_secondlist_prop { #1 }
 }

\NewDocumentCommand{\printlists}{ }
 {
  \section*{First~List}
  \__konstantin_print_list:N \g_konstantin_firstlist_prop
  \section*{Second~List}
  \__konstantin_print_list:N \g_konstantin_secondlist_prop
 }

\NewDocumentCommand{\maketickets}{}
 {
  % compute the number of questions
  \int_set:Nn \l__konstantin_index_int
   {
    \int_min:nn { \prop_count:N \g_konstantin_firstlist_prop }
                { \prop_count:N \g_konstantin_secondlist_prop }
   }
  % generate the identity permutation
  \int_step_inline:nnnn { 1 } { 1 } { \l__konstantin_index_int }
   {
    \prop_gput:Nnn \g_knuthshuffle_identity_prop { ##1 } { ##1 }
   }
  % generate a first permutation
  \knuthshuffle_generate:n { \l__konstantin_index_int }
  \seq_set_eq:NN \l_konstantin_firstquestion_seq \l_knuthshuffle_permutation_seq
  % generate a second permutation
  \knuthshuffle_generate:n { \l__konstantin_index_int }
  \seq_set_eq:NN \l_konstantin_secondquestion_seq \l_knuthshuffle_permutation_seq
  % print the tickets
  \int_step_inline:nnnn { 1 } { 1 } { \l__konstantin_index_int }
   {
    \konstantin_ticket:n { ##1 }
   }
 }

% variables
\int_new:N \l__konstantin_index_int
\prop_new:N \g_konstantin_firstlist_prop
\prop_new:N \g_konstantin_secondlist_prop

% internal functions
\cs_new_protected:Nn \__konstantin_define_list:Nn
 {
  \int_zero:N \l__konstantin_index_int
  \tl_map_inline:nn { #2 }
   {
    \int_incr:N \l__konstantin_index_int
    \prop_gput:Nfn #1 { \int_to_arabic:n { \l__konstantin_index_int } } { ##1 }
   }
 }
\cs_generate_variant:Nn \prop_gput:Nnn { Nf }

\cs_new_protected:Nn \__konstantin_print_list:N
 {
  \int_step_inline:nnnn { 1 } { 1 } { \prop_count:N #1 }
   {
    \noindent
    ##1.~
    \prop_item:Nn #1 { ##1 }
    \par
   }
 }

\cs_new_protected:Nn \konstantin_ticket:n
 {
  \clearpage
  \ticketheader{#1}
  \par
  \noindent\textbf{Question~1}:\\
  \prop_item:Nx \g_konstantin_firstlist_prop
   {
    \seq_item:Nn \l_konstantin_firstquestion_seq {#1}
   }
  \par
  \vspace*{\fill}
  \noindent\textbf{Question~2}:\\
  \prop_item:Nx \g_konstantin_secondlist_prop
   {
    \seq_item:Nn \l_konstantin_secondquestion_seq {#1}
   }
  \par
  \vspace*{\fill}
 }
\cs_generate_variant:Nn \prop_item:Nn { Nx }

%%% the Knuth shuffle macros https://tex.stackexchange.com/a/344488/4427

\prop_new:N \g_knuthshuffle_identity_prop
\prop_new:N \l_knuthshuffle_newperm_prop
\int_new:N \l_knuthshuffle_random_int
\seq_new:N \l_konstantin_firstquestion_seq
\seq_new:N \l_konstantin_secondquestion_seq

\cs_new:Nn \knuthshuffle_get_random:Nnn
 {
  \int_set:Nn #1 { \fp_eval:n { randint(#2,#3) } }
 }

\cs_new_protected:Nn \knuthshuffle_generate:n
 {
  \prop_set_eq:NN \l_knuthshuffle_newperm_prop \g_knuthshuffle_identity_prop
  \int_step_inline:nnnn { #1 } { -1 } { 2 }
   {
    \knuthshuffle_get_random:Nnn \l_knuthshuffle_random_int { 1 } { ##1 }
    \prop_get:NnN \l_knuthshuffle_newperm_prop { ##1 } \l_knuthshuffle_tempa_tl 
    \prop_get:NVN \l_knuthshuffle_newperm_prop \l_knuthshuffle_random_int \l_knuthshuffle_tempb_tl 
    \prop_put:NnV \l_knuthshuffle_newperm_prop { ##1 } \l_knuthshuffle_tempb_tl
    \prop_put:NVV \l_knuthshuffle_newperm_prop \l_knuthshuffle_random_int \l_knuthshuffle_tempa_tl
   }
  \seq_clear:N \l_knuthshuffle_permutation_seq
  \int_step_inline:nnnn { 1 } { 1 } { #1 }
   {
    \seq_put_right:Nx \l_knuthshuffle_permutation_seq
     {
      \prop_item:Nn \l_knuthshuffle_newperm_prop { ##1 }
     }
   }
  %\seq_show:N \l_knuthshuffle_permutation_seq % for debugging
 }


\ExplSyntaxOff

% define here the header for the question tickets
\newcommand{\ticketheader}[1]{% #1 is the ticket number
  \begin{center}
  \large\bfseries Ticket #1
  \end{center}
  \bigskip
}

% define the list of questions
\definefirstlist{
 {This, is}
 {is}
 {a}
 {very}
 {long}
 {text}
 {split}
 {in}
 {many}
}

\definesecondlist{
  {than}
  {thirty}
  {items}
  {in}
  {here}
  {but}
  {he}
  {did}
  {not}
}

\begin{document}

\printlists

\maketickets

\end{document}

enter image description here

6
  • This example is seems to be helpful for me but I can't compile it with MiKTeX 2.9 ! Undefined control sequence. <argument> \prop_count:N \g_konstantin_firstlist_prop l.168 \printlists
    – Konstantin
    Dec 30, 2016 at 6:18
  • 1
    @Konstantin Update your packages with the MiKTeX wizard
    – egreg
    Dec 30, 2016 at 10:37
  • I've updated the MiKTeX. The update process was not perfect and at first time ended with an error. The second attempt was successful (but on another machine I have not managed to update the MiKTeX). So at least one computer is compiled the code. But it does not work as I need. The questios are not formed in a random order, and if number of questions in first and secon part is defferent - last tickets forms with one question, enother is empty. And number of tickets can't exceed the number of questions.
    – Konstantin
    Jan 2, 2017 at 16:28
  • @Konstantin I used \int_max:nn, but it should have been \int_min:nn, now fixed. The questions are in random order.
    – egreg
    Jan 2, 2017 at 16:46
  • 1
    @Konstantin You were right, I forgot to use the generated sequences. Now it should be OK
    – egreg
    Jan 2, 2017 at 18:15

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .