latex3 抱怨未定义的控制序列。但它已定义!

latex3 抱怨未定义的控制序列。但它已定义!

我尝试过将其配对,但每次这样做时,我要么在纠正自己的错误,要么在做其他事情。所以这是一个略显笨拙的 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

其他要点。

  1. 当将变量声明为全局变量时,仅对其使用全局赋值。
  2. 不要忘记所有函数名称中都应有一个冒号。
  3. \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,我能够编写出比我一直忍受的循环解决方案更清晰的文档。

在此处输入图片描述

相关内容