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 '16 at 18:57
2

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 '16 at 6:18
  • @Konstantin Update your packages with the MiKTeX wizard
    – egreg
    Dec 30 '16 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 '17 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 '17 at 16:46
  • Code with \int_min generate tickets number of which equal to number of smaller qestions set. At the same time \int_max code generate tickets number of which equal to number of greater qestions set. The order in both cases are not random. I don't know how to attach .pdf for demonstrate it.
    – Konstantin
    Jan 2 '17 at 18:06
1

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 '21 at 7:12

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.