该etoolbox
包提供了处理列表的宏,这对包/类编写者很有用,但是它缺少获取随机子列表的宏。
所以我想定义以下宏:
\getrandomsublist{<to store list macro>}{<sublist length>}{<list macro>}
\getrandomsublistcs{<to store list macro>}{<sublist length>}{<list name>}
\getcsrandomsublist{<to store list name>}{<sublist length>}{<list macro>}
\getcsrandomsublistcs{<to store list name>}{<sublist length>}{<list name>}
首先,我尝试了以下操作:
\documentclass{article}
\usepackage{etoolbox}
\usepackage{pgfcore}
\usepackage{ifthen}
\usepackage{forloop}
%% this stores the length of a given list in the a counter, for later use
\newcounter{thelistlength}
\newcommand{\listlength}[1]{%
\setcounter{thelistlength}{0}%
\renewcommand{\do}{%
\stepcounter{thelistlength}%
}%
\dolistcsloop{#1}%
}
%% for test it with csv lists
\newcommand{\myinternallist}{}
\newcommand{\tomyinternallist}[1]{%
\forcsvlist{\listadd\myinternallist}{#1}%
}
%% \getrandomsublist{<sublist length>}{<list name>}
%% store the resulting sublist in a `\outputlist`
\newcommand{\outputlist}{}%
\newcommand{\getrandomsublist}[2]{%
\listlength{#2}% % calculate the length of the list
\newcommand{\randomindex}%
\newcounter{innerct}%
\setcounter{innerct}{0}%
\renewcommand{\do}[1]{%
\stepcounter{innerct}%
\ifnum \randomindex=\value{innerct}%
\listgadd{\outputlist}{##1}%
\fi%
}
\newcounter{ct}\forloop{ct}{0}{\value{ct}<#1}{%
\pgfmathrandominteger{\randomindex}{\value{thelistlength}}{1}%
\docslistloop{#2}%
}%
\outputlist
}
\begin{document}
\tomyinternallist{really,random,list,of,items}
\listlength{myinternallist}
\thethelistlength
\getrandomsublist{2}{myinternallist}
\renewcommand{\do}[1]{#1\qquad}
\dolistloop{\outputlist}
\end{document}
没有成功。
我们如何才能有效地定义这些宏?
答案1
我不会使用逗号分隔的列表,因为它们对于输入很方便,但是对于操作却很麻烦。
这是一个 LaTeX3 实现,其中每个列表都有一个名称;它的灵感来自我的答案在这里。
\documentclass{article}
\usepackage{xparse}
\input{random}
\ExplSyntaxOn
\NewDocumentCommand{\definelist}{mm}
{
\leo_define_list:nn {#1} {#2}
}
\cs_new_protected:Npn \leo_define_list:nn #1 #2
{
\seq_gset_split:cnn { g_leo_list_ #1 _seq } { , } { #2 }
}
\cs_generate_variant:Nn \seq_gset_split:Nnn { c }
\NewDocumentCommand{\getrandomsublist}{mmm}
{
\leo_get_random_sublist:nNN {#1} {#2} {#3}
}
\cs_new_protected:Npn \leo_get_random_sublist:nNN #1 #2 #3
{
\seq_clear:N \l_leo_used_seq
\seq_gclear_new:c { g_leo_list_#2_seq }
\int_set:Nn \l_leo_length_int { \seq_count:c { g_leo_list_#3_seq } }
\int_compare:nTF { #1 > \l_leo_length_int }
{
\msg_error:nnxx { randomchoice } { too-many } { #1 } { \int_to_arabic:n { \l_leo_length_int } }
}
{
\int_step_inline:nnnn { 1 } { 1 } { #1 }
{
\leo_get_random:
\seq_gput_right:cx { g_leo_list_#2_seq}
{ \seq_item:cn { g_leo_list_#3_seq } { \l_leo_random_int } }
}
}
}
\cs_new_protected:Npn \leo_get_random:
{
\setrannum { \l_leo_random_int } { 1 } { \l_leo_length_int }
\seq_if_in:NxTF \l_leo_used_seq { \int_to_arabic:n { \l_leo_random_int } }
{ \leo_get_random: }
{ \seq_put_right:Nx \l_leo_used_seq { \int_to_arabic:n { \l_leo_random_int } } }
}
\seq_new:N \l_leo_used_seq
\clist_new:N \l_leo_temp_clist
\int_new:N \l_leo_length_int
\int_new:N \l_leo_random_int
\msg_new:nnnn { randomchoice } { too-many }
{ Too~ many~choices }
{ You~want~to~select~#1~elements,~but~you~have~only~#2 }
%%% Printing the new list
\NewDocumentCommand{\printlist}{m}
{
\seq_use:cnnn { g_leo_list_#1_seq } {,} {,} {,}
}
\cs_generate_variant:Nn \seq_use:Nnnn { c }
\ExplSyntaxOff
\begin{document}
\definelist{myinternallist}{really,random,list,of,items,with,many,objects,in,it}
\getrandomsublist{2}{randomlisti}{myinternallist}
\getrandomsublist{3}{randomlistii}{myinternallist}
\getrandomsublist{4}{randomlistiii}{myinternallist}
\getrandomsublist{5}{randomlistiv}{myinternallist}
\getrandomsublist{10}{randomlistv}{myinternallist}
\ttfamily
\printlist{randomlisti}
\printlist{randomlistii}
\printlist{randomlistiii}
\printlist{randomlistiv}
\printlist{randomlistv}
\end{document}
当然,你可以用不同的方式使用序列,比如从数据库中获取项目。如果\getitem
是宏,那么\getitem{foo}
可以使用数据库中的项目foo
,你可以定义
\NewDocumentCommand{\uselist}{m}
{
\seq_map_inline:cn { g_leo_list_#1_seq } { \getitem{##1} }
}
在序言中说
\getrandomsublist{2}{randomlisti}{myinternallist}
\uselist{randomlisti}
因为\seq_map_inline:cn
将一次遍历新建的序列中的一个项目。