按顺序和随机(不重复)顺序显示项目列表

按顺序和随机(不重复)顺序显示项目列表

这里有考试题目列表(例如 1-20)。该列表分为两部分(1-10、11-20)。试卷包含两个问题,第一个来自第一部分,第二个来自第二部分。这些问题必须随机选择且不重复。随机选择且不重复的问题之前已使用\pgfmathdeclarerandomlist命令解决。

但考试之前生成的试题列表必须按顺序显示。因此列表定义为:

\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}}

出于随机目的:

%вопросы первого блока
\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
}

在文档顺序列表中显示为:

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

考试票上有:

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

但编译时出现错误:

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

当我尝试\printfirst或时发生此错误\printsecond。据我了解,此错误与列表分隔符有关|。我该如何修复它?

答案1

在 LaTeX 中处理数组的最简单方法是使用\csname ...\endcsname。(您也可以使用xstring包,但这样速度更快,尽管不太好看。)问题是 必须在或\csname之类的东西之前展开,导致出现很多s。此外,不是合法的宏名称,只能使用 来实现。\let\xdef\expandafter\index0\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}

答案2

以下是使用 Knuth shuffle 算法的实现(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}

在此处输入图片描述

相关内容