我正在尝试掌握 LaTeX3 的来龙去脉。我想做以下事情:我定义了一个randomizechoices
用于NewDoucmentEnvironment
抓取内容的环境。然后解析此内容。解析时,我想将标记列表拆分为一个标记(序列),然后随机化该序列,然后重新构建标记列表。然后排版此标记列表。现在随机化标记列表不是直接可用的,但随机化序列是可用的。所以我需要将标记列表转换为(拆分)序列,将其随机化并重新构建标记列表(或以任何方式执行此操作)。MWE 如下所示:
\documentclass{exam}
\usepackage{xparse}
\ExplSyntaxOn
% for now
\NewDocumentCommand{\inaccessible}{}{X}
\NewDocumentEnvironment{randomizechoices}{ +!b }
{
\tl_set:Nn \l_my_tl {#1}
\tl_replace_all:Nnn \l_my_tl {\CorrectChoice} {\choice \inaccessible}
\tl_replace_all:Nnn \l_my_tl {\correctchoice} {\choice \inaccessible}
% shuffle on \choice in token list
% - or -
% Convert to sequence on \choice, shuffle, rebuild
% - or -
% Convert to string, split on \choice to sequence, shuffle, rebuild
%
\begin{choices}
\tl_use:N \l_my_tl
\end{choices}
}
{}
\ExplSyntaxOff
\begin{document}
\begin{questions}
\question
Blah
\begin{randomizechoices}
\choice Hello
\choice World!
\CorrectChoice and
\choice everyone!
\end{randomizechoices}
\question
Blah
\begin{randomizechoices}
\choice Hello
\choice World!
\correctchoice and
\choice everyone!
\end{randomizechoices}
\end{questions}
\end{document}
答案1
下面通过使用序列来实现这一点。
\documentclass{exam}
\usepackage{xparse}
\ExplSyntaxOn
% for now
\NewDocumentCommand{\inaccessible}{}{X}
% initialize the used variables
\tl_new:N \l_my_body_tl
\seq_new:N \l_my_body_seq
\NewDocumentEnvironment{randomizechoices}{ +!b }
{
\tl_set:Nn \l_my_body_tl {#1}
\tl_replace_all:Nnn \l_my_body_tl {\CorrectChoice} {\choice \inaccessible}
\tl_replace_all:Nnn \l_my_body_tl {\correctchoice} {\choice \inaccessible}
% set sequence split at \choice
\seq_set_split:NnV \l_my_body_seq { \choice } \l_my_body_tl
% remove empty elements
\seq_remove_all:Nn \l_my_body_seq {}
\seq_shuffle:N \l_my_body_seq
% put back randomized sequence with \choice between the items
\tl_set:Nx \l_my_body_tl
{
\exp_not:N \choice
\seq_use:Nn \l_my_body_seq \choice
}
\begin{choices}
\tl_use:N \l_my_body_tl
\end{choices}
}
{}
\ExplSyntaxOff
\begin{document}
\begin{questions}
\question
Blah
\begin{randomizechoices}
\choice Hello
\choice World!
\CorrectChoice and
\choice everyone!
\end{randomizechoices}
\question
Blah
\begin{randomizechoices}
\choice Hello
\choice World!
\correctchoice and
\choice everyone!
\end{randomizechoices}
\end{questions}
\end{document}
这是基于@egreg 的精彩回答但改变了它的一些简单方面:
- 我把标记改成了夸克
- 我将该标记的测试更改为完全可扩展,这样用作输出的序列仅包含一次内容而不是三次(恕我直言,更干净的代码)
- 增加放置标签的可能性(仅当你将种子设置
expl3
为固定值时才有意义)
\documentclass{exam}
\ExplSyntaxOn
\sys_gset_rand_seed:n { \fp_eval:n { round(1e6 * pi) } }
\NewDocumentEnvironment{randomizechoices}{ o +b }
{ \jodb_choices_randomize:nn {#1} {#2} }
{}
% initialize the used variables
\tl_new:N \l_jodb_choices_body_tl
\seq_new:N \l_jodb_choices_body_in_seq
\seq_new:N \l_jodb_choices_body_out_seq
\quark_new:N \q__jodb_choices_marker
\cs_set_protected:Npn \jodb_choices_randomize:nn #1#2
{
\tl_set:Nn \l_jodb_choices_body_tl {#2}
\tl_replace_all:Nnn \l_jodb_choices_body_tl
{\CorrectChoice} {\choice \q__jodb_choices_marker}
\tl_replace_all:Nnn \l_jodb_choices_body_tl
{\correctchoice} {\choice \q__jodb_choices_marker}
\seq_set_split:NnV \l_jodb_choices_body_in_seq
{ \choice } \l_jodb_choices_body_tl
% first item is empty
\seq_pop_left:NN \l_jodb_choices_body_in_seq \l_tmpa_tl
% randomize
\seq_shuffle:N \l_jodb_choices_body_in_seq
% build another sequence
\seq_set_map_x:NNn \l_jodb_choices_body_out_seq \l_jodb_choices_body_in_seq
{
\tl_if_head_eq_meaning:nNTF { ##1 } \q__jodb_choices_marker
{
\exp_not:n { \CorrectChoice }
\IfValueT {#1} { \exp_not:n { \label{#1} } }
\exp_not:o { \use_none:n ##1 }
}
{ \exp_not:n { \choice ##1 } }
}
\begin{choices}
\seq_use:Nn \l_jodb_choices_body_out_seq { }
\end{choices}
}
\ExplSyntaxOff
\begin{document}
\printanswers
\begin{questions}
\question
Blah
\begin{randomizechoices}[ans:blah]
\choice Hello
\choice World!
\CorrectChoice and
\choice everyone!
\end{randomizechoices}
\question
Blah (but see also \ref{ans:blah})
\begin{randomizechoices}
\choice Hello
\choice World!
\correctchoice and
\choice everyone!
\end{randomizechoices}
\end{questions}
\end{document}
答案2
密钥是\seq_set_split:NnV
,因此序列由以分隔的项目填充,\choice
并且可以随机化。
\choice
然后您可以通过重新插入或\CorrectChoice
(如果项目中存在标记)来重建序列。
\documentclass{exam}
\ExplSyntaxOn
\NewDocumentEnvironment{randomizechoices}{ +b }
{
\jodb_choices_randomize:n { #1 }
}
{}
% initialize the used variables
\tl_new:N \l_jodb_choices_body_tl
\seq_new:N \l_jodb_choices_body_in_seq
\seq_new:N \l_jodb_choices_body_out_seq
\cs_new_protected:Nn \__jodb_choices_marker: { }
\cs_set_protected:Nn \jodb_choices_randomize:n
{
\tl_set:Nn \l_jodb_choices_body_tl {#1}
\tl_replace_all:Nnn \l_jodb_choices_body_tl {\CorrectChoice} {\choice \__jodb_choices_marker:}
\tl_replace_all:Nnn \l_jodb_choices_body_tl {\correctchoice} {\choice \__jodb_choices_marker:}
\seq_set_split:NnV \l_jodb_choices_body_in_seq { \choice } \l_jodb_choices_body_tl
% first item is empty
\seq_pop_left:NN \l_jodb_choices_body_in_seq \l_tmpa_tl
% randomize
\seq_shuffle:N \l_jodb_choices_body_in_seq
% build another sequence
\seq_set_map_x:NNn \l_jodb_choices_body_out_seq \l_jodb_choices_body_in_seq
{
\tl_if_in:nnTF { ##1 } { \__jodb_choices_marker: }
{ \exp_not:n { \CorrectChoice ##1 } }
{ \exp_not:n { \choice ##1 } }
}
\begin{choices}
\seq_use:Nn \l_jodb_choices_body_out_seq { }
\end{choices}
}
\ExplSyntaxOff
\begin{document}
\printanswers
\begin{questions}
\question
Blah
\begin{randomizechoices}
\choice Hello
\choice World!
\CorrectChoice and
\choice everyone!
\end{randomizechoices}
\question
Blah
\begin{randomizechoices}
\choice Hello
\choice World!
\correctchoice and
\choice everyone!
\end{randomizechoices}
\end{questions}
\end{document}
抱歉,但为正确的选择添加标签是行不通的,因为每次运行 LaTeX 时引用通常都会发生变化。