我尝试过将其配对,但每次这样做时,我要么在纠正自己的错误,要么在做其他事情。所以这是一个略显笨拙的 MWE。
简而言之,我想做的是......
我正在编写一个有多个版本的测验。我有一些笨重的 LaTeX 代码,它们已经处理了版本控制,但我认为,如果我尝试使用 LaTeX3 编写一些内容,我可能会得到更清晰的文档代码。
每个测验都附带一个版本。我正在编写一个\choice
宏,它给出了;
可能输出的单独列表,这些输出将根据测验的版本而变化。
例如如果我写,\choice{x;y;z;w}
那么在测验的 A 版本上将\choice
提供x
,在测验的 B 版本上将\choice
提供y
,等等。
我还远没有开始使用它。我其实对别人告诉我如何实现我的最终目标不感兴趣:我认为如果我自己尝试,我会学到很多东西。另外,我知道我没有完全正确地遵循符号惯例,但试图平衡符号(我仍然觉得很难读懂)和掌握语法(我觉得有点晦涩,但开始掌握了)让我很头疼。
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
%-@-(1)---------------------------------------------------------------------
%% --- VERSIONING --- %%
%% store what the possible version are
\tl_new:N \g__version_types
\tl_set:Nn \g__version_types { a;b;c;d }
\tl_show:N \g__version_types
%% make a sequence of the possible versions
\seq_new:N \g__all_possible_versions_seq
\seq_gset_split:Nnn \g__all_possible_versions_seq { ; } {\g__version_types}
\seq_show:N \g__all_possible_versions_seq
%% allow the user to define what the versions are
\NewDocumentCommand{\defineversions}{ O{;} m }{
\tl_set:Nn \g__version_types { #2 }
\seq_set_split:Nnn \g__all_possible_versions_seq { #1 } \g__version_types
}
%-@-(2)---------------------------------------------------------------------
%% --- GETTING/SETTING VERSIONS ---%%
\tl_new:N \g__tl_current_doc_version
\cs_new:Npn \mv_set_version:n #1
{
\tl_set:Nn \g__tl_current_doc_version { #1 }
}
\cs_new:Npn \mv_get_version
{
\tl_use:N \g__tl_current_doc_version
}
\newcommand{\setversion}[1]{\mv_set_version:n {#1}}
\newcommand{\getversion}{ \mv_get_version }
\cs_new:Npn \mv_test_version:n #1 {
\str_if_eq:VnTF \g__tl_current_doc_version { #1 } { HELLO } { BYE}
}
\newcommand{\testversion}[1]{\mv_test_version:n {#1}}
%-@-(3)---------------------------------------------------------------------
%% --- CREATING THE USER INTERFACE --- %%
%% I'm going to destructive examine the sequence, so make
%% a copy of it and work with copy
\seq_new:N \g__copy_all_possible_versions_seq
\seq_set_eq:NN \g__copy_all_possible_versions_seq \g__all_possible_versions_seq
\tl_new:N \g__current_possible_version_tl
%% Information that the user passes to us
\seq_new:N \g__user_defined_choice_seq
\tl_new:N \g__current_possible_choice_tl
%% This "cs" assumes that the user choices have been translated
%% into a sequence
\cs_new:Npn \__test_current_choice_against_version:n #1 {
\seq_pop_left:NN \g__user_defined_choice_seq
\g__current_possible_choice_tl
\seq_pop_left:NN \g__copy_all_possible_versions_seq
\g__current_possible_version_tl
\tl_use:N \g__current_possible_choice_tl --
\tl_use:N \g__current_possible_version_tl \par
\str_if_eq:VVTF \g__current_possible_choice_tl
\g__current_possible_version_tl
{ \tl_use:N \g__current_possible_choice_tl }
{ #1 }
}
\cs_new:Npn \mv_make_choice:n #1 {
\__test_current_choice_against_version:n
{ \__test_current_choice_against_version:n
{ \__test_current_choice_against_version:n
{ FAIL }}}
}
\NewDocumentCommand{\choices}{ O{;} m }{
\texttt{#2}\par
\seq_gset_split:Nnn \g__user_defined_choice_seq {#1} {#2}
\mv_make_choice:n {#1}
}
\ExplSyntaxOff
\begin{document}
Hello: I'm setting the version \setversion{b}
I'm getting the version \textbf{\getversion}!
Choosing \choices{x;y;z;w}
\end{document}
我收到有关空序列的错误。我尝试显示该序列以及用于创建该序列的标记,但似乎都不正确。
答案1
我试图纠正它,但我不知道它是否能满足你的要求:
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
%-@-(1)---------------------------------------------------------------------
%% --- VERSIONING --- %%
%% store what the possible version are
\tl_new:N \g__version_types_tl
\tl_set:Nn \g__version_types_tl { a;b;c;d }
%% make a sequence of the possible versions
\seq_new:N \g__all_possible_versions_seq
\seq_new:N \g__copy_all_possible_versions_seq
\cs_generate_variant:Nn \seq_gset_split:Nnn { NnV }
\seq_gset_split:NnV \g__all_possible_versions_seq { ; } \g__version_types_tl
\seq_gset_eq:NN \g__copy_all_possible_versions_seq \g__all_possible_versions_seq
%% allow the user to define what the versions are
\NewDocumentCommand{\defineversions}{ O{;} m }
{
\tl_gset:Nn \g__version_types_tl { #2 }
\seq_gset_split:NnV \g__all_possible_versions_seq { #1 } \g__version_types_tl
\seq_gset_eq:NN \g__copy_all_possible_versions_seq \g__all_possible_versions_seq
}
%-@-(2)---------------------------------------------------------------------
%% --- GETTING/SETTING VERSIONS ---%%
\tl_new:N \g__current_doc_version_tl
\cs_new_protected:Npn \mv_set_version:n #1
{
\tl_set:Nn \g__current_doc_version_tl { #1 }
}
\cs_new:Npn \mv_get_version:
{
\tl_use:N \g__current_doc_version_tl
}
\NewDocumentCommand{\setversion}{m}
{
\mv_set_version:n {#1}
}
\NewDocumentCommand{\getversion} {}
{
\mv_get_version:
}
\cs_new:Npn \mv_test_version:n #1
{
\tl_if_eq:VnTF \g__tl_current_doc_version { #1 } { HELLO } { BYE}
}
\NewDocumentCommand{\testversion}{m}
{
\mv_test_version:n {#1}
}
%-@-(3)---------------------------------------------------------------------
%% --- CREATING THE USER INTERFACE --- %%
%% I'm going to destructively examine the sequence, so make
%% a copy of it and work with copy
\tl_new:N \g__current_possible_version_tl
%% Information that the user passes to us
\seq_new:N \g__user_defined_choice_seq
\tl_new:N \g__current_possible_choice_tl
%% This "cs" assumes that the user choices have been translated
%% into a sequence
\cs_new_protected:Npn \__test_current_choice_against_version:
{
%I'm working
\seq_gpop_left:NN \g__user_defined_choice_seq
\g__current_possible_choice_tl
\seq_gpop_left:NN \g__copy_all_possible_versions_seq
\g__current_possible_version_tl
\tl_if_eq:NNTF \g__current_possible_choice_tl
\g__current_possible_version_tl
{ \tl_use:N \g__current_possible_choice_tl }
{ FAIL }
}
\cs_new:Npn \mv_make_choice:n #1
{
\__test_current_choice_against_version:
}
\NewDocumentCommand{\choices}{ O{;} m }
{
\texttt{#2}\par
\seq_gset_split:Nnn \g__user_defined_choice_seq {#1} {#2}
\mv_make_choice:n {#1}
}
\ExplSyntaxOff
\begin{document}
Hello: I'm setting the version \setversion{b}
I'm getting the version \textbf{\getversion}!
Choosing \choices{x;y;z;w}
\end{document}
我修复了名称和一些编程错误。例如,您正在比较标记列表,\str_if_eq:...
但\tl_if_eq:...
应该使用。特别是\tl_if_eq:NNTF
,而不是\str_if_eq:VVTF
哪个会无缘无故地做更多的工作。你为什么用\mv_make_choice:n
一个你不使用的参数来定义,这很模糊。只需用作\__test_current_choice_against_version:
中的最后一条指令即可\choices
。
其他要点。
- 当将变量声明为全局变量时,仅对其使用全局赋值。
- 不要忘记所有函数名称中都应有一个冒号。
\cs_new_protected:Npn
当函数执行不可扩展的工作(例如设置标记列表或序列)时使用。
答案2
我非常感谢大家给我的反馈。如果没有你们的建议,我不确定我是否能想出一个可行的例子。由于你们投入了时间和精力,我想发布我的想法。
此时,如果您发现某些风格上您确实不喜欢的东西,我很乐意听取反馈。我想我更了解发生了什么。@egreg。我尝试遵循您关于\tl_if_eq
和的建议\str_if_eq
,我认为在以下代码中我确实需要这样做。考虑到您的评论和建议,我会将您的答案标记为正确答案。
编辑
鉴于我已经介绍了尽可能多的解决方案,我认为我应该将其更新为可以进行一些简单错误检查的版本。它仍然可以进行一些改进,但我认为这将是我的最终编辑。
\documentclass{article}
\pagestyle{empty}
\usepackage{amsmath,amssymb,xcolor}
\usepackage{xparse}
\ExplSyntaxOn
%% ----------------- PARAMETERS ------------------ %%
%% Creating versions and their default values %%
\seq_new:N \g__possible_version_types_seq
\seq_gset_split:Nnn \g__possible_version_types_seq {;} { a;b;c;d }
\tl_new:N \g__current_version_tl
\tl_set:Nn \g__current_version_tl {a}
%% Creating choices and a means saving choice for %%
%% later use: even if defined within a group %%
\seq_new:N \g__user_provided_choices_seq
\tl_new:N \g__selected_choice_tl
%% this next token is to allow `\selectedchoice` %%
%% to be definable from within a grouping. %%
\tl_new:N \g__callable_selected_choice_tl
%% a function to select the choice corresponding %%
%% to the current version %%
\cs_new:Npn \fnc_match_version_to_choices:nn #1#2 {
\str_if_eq:nVT {#1} \g__current_version_tl {\tl_gset:Nn \g__selected_choice_tl {#2}}
}
%% --------------- ERROR CHECKING --------------- %%
%% Make sure you don't try to pass a version that %%
%% has not yet been defined. %%
\msg_new:nnnn {mymodule}{invalid version call}{You've\ called\ for\ a\ non-existant\ version.}{}
\cs_new:Nn \err_am_i_defining_valid_version: {
\seq_if_in:NVF \g__possible_version_types_seq \g__current_version_tl
{ \msg_error:nn{mymodule}{invalid version call}}
}
%% Make sure you provide choices to correspond %%
%% with the number of versions. Having more %%
%% choices than versions will not signal an error. %%
\msg_new:nnnn {mymodule}{unequalchoices}{There\ are\ more\ versions\ than\ choices}{}
\cs_new:Nn \err_fewer_choices_than_versions: {
\int_compare:nT {
\seq_length:N \g__possible_version_types_seq
>
\seq_length:N \g__user_provided_choices_seq
}
{\msg_error:nn{mymodule}{unequalchoices}}
}
%% ---------------- USER INTERFACE ---------------%%
%% allow the user to set the version types %%
\NewDocumentCommand{\setpossibleversions}{O{;} m}{
\seq_gset_split:Nnn \g__possible_version_types_seq {#1} {#2}
}
%% allow the user to set the version manually %%
\NewDocumentCommand{\setversion}{m}{
\tl_gset:Nn \g__current_version_tl {#1}
\err_am_i_defining_valid_version:
}
%% user interface to map choices to version of quiz %%
%% %%
%% make sure that you're not expecting more versions %%
%% than you've provided choices for. %%
%% %%
%% (1) pair "versions" with "choices" %%
%% (2) match "version" against "current version" to %%
%% select the desired choice %%
%% (3) if the same, return "selected choice" %%
%% %%
%% NOTE: the starred version of `\choice` saves the %%
%% value of the choice to `\selectedchoice` so you %%
%% can access it later %%
\NewDocumentCommand{\choices}{ s O{;} m }{
\seq_gset_split:Nnn \g__user_provided_choices_seq {#2} {#3}
\err_fewer_choices_than_versions:
\seq_mapthread_function:NNN \g__possible_version_types_seq
\g__user_provided_choices_seq
\fnc_match_version_to_choices:nn
\tl_use:N \g__selected_choice_tl
\IfBooleanT #1 {\tl_gset:NV \g__callable_selected_choice_tl \g__selected_choice_tl}
\tl_gset:Nn \g_selected_choice_tl {}
}
\newcommand{\selectedchoice}{\tl_use:N \g__callable_selected_choice_tl}
%% you might want access to the current version to %%
%% set page numbers %%
\NewDocumentCommand{\getversion}{}{\tl_use:N \g__current_version_tl}
\ExplSyntaxOff
\begin{document}
\setpossibleversions[,]{a,b,c,d,e}
\setversion{a}
If $m\angle\choices{1;2;3;4;5}=\choices[,]{40,42,38,45,25}^\circ$, then the
\choices*{supplement;complement;complement;supplement;supplement} of the angle has measure\ldots
The measure of the \selectedchoice\ is...
\setversion{d}
If $m\angle\choices*{1;2;3;4;5}=\choices[,]{40,42,38,45,25}^\circ$, then the
\choices{supplement;complement;complement;supplement;supplement} of the angle has measure\ldots
Notice how the measure of $\angle{\selectedchoice}$ is smaller than 90.
This is a clue.
\end{document}
无论风格如何,借助 LaTeX3,我能够编写出比我一直忍受的循环解决方案更清晰的文档。