从逗号分隔的列表中选择随机元素

从逗号分隔的列表中选择随机元素

我想要一个具有基本形式的命令(用户定义的,或者来自包的,或者其他什么):

\selectNrandom{N}{list, of, comma, separated, elements}{code to execute}

这将选择 N清楚的,从后面的 csv 列表中随机选取元素,然后对这些元素执行代码。

例如,这将选择两个元素,然后在它们之间留出较大的空间(想想数学测验):

\selectNrandom{2}{N, W, Z, Q, R, C}{%
    \mathbb{firstElement} \qquad  \mathbb{secondElement}
    }

除了上述内容之外,我不知道还要发布什么类型的 MWE,因为我真的不知道如何开始编写宏来执行此操作。

提出要求的理由(即我做过做我的家庭作业!):我一直在阅读各种软件包文档(etextools、probsoln、datatool 等)以及一些书籍(Joy of TeX、The Advanced TeXbook)和各种网站等资料。但我对 LaTex 和 Tex 的整个编程方面仍然很陌生。

答案1

这是带有 LaTeX3 代码的版本xparse,借助random.texD. Arsenau 的文件

\documentclass{article}
\usepackage{xparse}
\input{random}

\ExplSyntaxOn
\NewDocumentCommand{\htguse}{ m }
 {
  \use:c { htg_arg_#1: }
 }
\NewDocumentCommand{\selectNrandom}{ m m m }
 {
  \htg_select_n_random:nnn { #1 } { #2 } { #3 }
 }

\cs_new_protected:Npn \htg_select_n_random:nnn #1 #2 #3
 {
  \seq_clear:N \l_htg_used_seq
  \int_set:Nn \l_htg_length_int { \clist_count:n { #2 } }
  \int_compare:nTF { #1 > \l_htg_length_int }
   {
    \msg_error:nnxx { randomchoice } { too-many } { #1 } { \int_to_arabic:n { \l_htg_length_int } }
   }
   {
    \int_step_inline:nnnn { 1 } { 1 } { #1 }
     {
      \htg_get_random:
      \cs_set:cpx { htg_arg_##1: }
       { \clist_item:nn { #2 } { \l_htg_random_int } }
     }
    #3
   }
 }
\cs_new_protected:Npn \htg_get_random:
 {
  \setrannum { \l_htg_random_int } { 1 } { \l_htg_length_int }
  \seq_if_in:NxTF \l_htg_used_seq { \int_to_arabic:n { \l_htg_random_int } }
   { \htg_get_random: }
   { \seq_put_right:Nx \l_htg_used_seq { \int_to_arabic:n { \l_htg_random_int } } }
 }
\seq_new:N \l_htg_used_seq
\int_new:N \l_htg_length_int
\int_new:N \l_htg_random_int
\msg_new:nnnn { randomchoice } { too-many }
 { Too~ many~choices }
 { You~want~to~select~#1~elements,~but~you~have~only~#2 }
\ExplSyntaxOff

\begin{document}

\selectNrandom{2}
  {N, W, Z, Q, R, C}
  {$\mathbf{\htguse{1}}$ and $\mathbf{\htguse{2}}$}

\selectNrandom{3}
  {A, B, C}
  {$\mathbf{\htguse{1}}$, $\mathbf{\htguse{2}}$ and $\mathbf{\htguse{3}}$}

\selectNrandom{3}
  {N, W}
  {$\mathbf{\htguse{1}}$, $\mathbf{\htguse{2}}$ and $\mathbf{\htguse{3}}$}

\end{document}

宏会通过维护已提取元素的列表来检查是否选择了不同的元素,如果再次提取数字,则进行新的选择。

您通过 引用第一个、第二个等等元素\htguse{1}\htguse{2}等等。

第三次调用将引发错误:

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!
! randomchoice error: "too-many"
! 
! Too many choices
! 
! See the randomchoice documentation for further information.
! 
! For immediate help type H <return>.
!...............................................  

l.56 ...bf{\htguse{2}}$ and $\mathbf{\htguse{3}}$}

? h
|'''''''''''''''''''''''''''''''''''''''''''''''
| You want to select 3 elements, but you have only 2
|...............................................

在此处输入图片描述

使用较新的expl3内核,\input{random}不再需要pdflatex或 LuaLaTeX(对于 XeLaTeX 来说,它仍然是必需的)。该行

\setrannum { \l_htg_random_int } { 1 } { \l_htg_length_int }

可以替换为

\int_set:Nn \l_htg_random_int { \fp_eval:n { randint( \l_htg_length_int ) } }

答案2

这是一个 Lua 解决方案,所选参数可以通过#1#2等方式访问。由于我还没有完全弄清楚 TeX 和 lua 在扩展方面的交互,因此我不能确定使用#1等的行为方式与您在常规宏定义中预期的方式相同。

在此处输入图片描述

\documentclass{article}
\usepackage{luacode}

\begin{luacode*}
local rand = math.random
local args = {}
function getnrand(n,l,f)
    tab = string.explode(l,",")
    for i = 1,n do
        local num = rand(#tab)
        args[i] = tab[num]
        table.remove(tab,num)
    end
    f = string.gsub(f,"#(%d+)",
            function(n)
                return tostring(args[tonumber(n)])
            end)
    tex.sprint(f)
end
\end{luacode*}

\long\def\selectNrandom#1#2#3{\directlua{getnrand(#1,"\luatexluaescapestring{\unexpanded{#2}}","\luatexluaescapestring{\unexpanded{#3}}")}}

\begin{document}

\selectNrandom{3}{a,b,c,d,e,f,g}{
The first random letter is: \textbf{#1}

The second random letter is: \textbf{#2}

The third random letter is: \textbf{#3}
}

\end{document}

相关内容