附录

附录

我不明白我在这里做错了什么。注释行都不如未注释行有效。

\begin{filecontents}{silly.tex}
    This is just a test.
\end{filecontents}
\documentclass{article}
\usepackage{xparse}
\NewDocumentCommand{\setversion}{ m }
    {
            Hello I'm version \texttt{#1}.\par        
    }
\ExplSyntaxOn
\cs_generate_variant:Nn \seq_set_split:Nnn {NVV}
\DeclareExpandableDocumentCommand{\makeversions}{ somm }
%\NewDocumentCommand{\makeversions}{ somm }
    {
        \bool_set:Nn \l_ae_makeversions_star_bool       { #1 }
        \tl_set:Nn   \l_ae_makeversions_delimiter_tl    { #2 }
        \tl_set:Nn   \l_ae_makeversions_versionlist_tl  { #3 }
        \tl_set:Nn   \l_ae_makeversions_inputfile_tl    { #4 }
        \exp_args:NV \IfNoValueTF \l_ae_makeversions_delimiter_tl 
                    {
                        TRUE:I have no value:\par
                        \tl_set:Nn \l_ae_makeversions_delimiter_tl { ; }
                    }
                    {
                        FALSE:\l_ae_makeversions_delimiter_tl \par
                    }
                    Delimiter ~ is ~ \l_ae_makeversions_delimiter_tl \par
%% In the following, I want the first commented out line of code to work.
%% Only one of the next FOUR lines should be left uncommented.
%        \seq_set_split:NVV \l_ae_version_seq  \l_ae_makeversions_delimiter_tl  \l_ae_makeversions_versionlist_tl 
%        \seq_set_split:NnV \l_ae_version_seq { ; } \l_ae_makeversions_versionlist_tl 
%        \seq_set_split:Nxn \l_ae_version_seq { \tl_use:N \l_ae_makeversions_delimiter_tl  }{ #3 }
        \seq_set_split:Nnn \l_ae_version_seq { ;  }{ #3 }
        \seq_map_inline:Nn \l_ae_version_seq { 
                                                 \setversion{##1}
                                                 \input{  \l_ae_makeversions_inputfile_tl }
                                                 \IfBooleanT { \l_ae_makeversions_star_bool } 
                                                             { \par\clearpage }
                                              }
    }

\ExplSyntaxOff
\pagestyle{empty}
\begin{document}

\makeversions*{a;b;c;d}{silly}

\end{document}

我曾尝试生成各种变体\seq_set_split:N**但都无济于事。

附录

我非常清楚我可以定义\makeversions如下:

\NewDocumentCommand{\makeversions}{ s O{;} mm }
    {
        \tl_set:Nn   \l_ae_makeversions_versionlist  { #3 }
        \tl_set:Nn   \l_ae_makeversions_input_file   { #4 }
        \seq_set_split:Nnn \l_ae_version_seq { ;  }{ #3 }
        \seq_map_inline:Nn \l_ae_version_seq { 
                                                 \setversion{##1}
                                                 \input{#4}
                                                 \IfBooleanT { #1 } 
                                                             { \par\clearpage }
                                              }
    }

但我想提醒读者,这是一个 MWE。我的原始代码比上面的例子要复杂得多。第一个 MWE 仅仅说明了我如何设法将我的原始问题归结为适合本网站的简短而简洁的内容。

在我的原始\makeversions函数中,调用了另一个上下文相关的函数。这个新函数,我们称之为,\ae_makeversion_by_context对如何处理各种参数做出各种决定,然后将工作分派给其他几个宏,然后由它们做出进一步的决定。

例如,分隔符。当我改变上下文时,默认设置也会改变(这听起来可能表面上很奇怪,但对于我的团队需要创建的文档类型来说,这是有意义的)。但是,我也希望允许用户覆盖此上下文相关的默认值

根据我收到的回复,我开始认为,关于分隔符,我可能应该创建一个布尔变量。也就是说,我可以写类似

\bool_new:N \g_ae_makeversions_delimiter_override_bool
\NewDocumentCommand{\makeversions}{ somm }
    {
        \IfNoValueTF {#2}
                    {
                        \bool_gset_true:N \g_ae_makeversions_delimiter_override_bool
                    }
                    {
                        \bool_gset_false:N \g_ae_makeversions_delimiter_override_bool
                    }
        ... < other code> ....    
    }

这或许是一个更好的设计决策。

所以我最初的问题归结为以下两个问题:

  • \IfNoValue如果传递的是令牌而不是原始输入变量,我该如何返回正确的值#2
  • 我如何才能seq_set_split正确解释其第二和第三个参数中的标记?

我把这些问题放在一起是因为乍一看,它们似乎围绕着同一个问题。

@ScottH 很好地回答了这两个问题。我怀疑其中还存在一些更深层次的问题\IfNoValue。因此我发布了解决该问题的新问题

我希望这能更好地解释我的一些明显的糟糕的设计决策。

答案1

同一问题出现两次:在需要参数n类型时使用参数类型。当指定 或“无操作”参数类型时,宏需要一个括号组,其内容将原封不动地传递给宏。当VnV指定参数类型时,宏需要一个变量名作为参数,并且该变量的值或替换文本被传递给宏。比较一下,

\tl_set:Nn \l_tmpa_tl {contents}

\tl_set:Nn \l_tmpb_tl {\l_tmpa_tl}
\tl_show:N \l_tmpb_tl

\tl_set:NV \l_tmpb_tl \l_tmpa_tl
\tl_show:N \l_tmpb_tl

现在首先发生这种情况的是

\IfNoValueT { \l_ae_makeversions_delimiter }

\IfNoValueT定义为

\cs_new_eq:NN \IfNoValueT \__xparse_if_no_value:nT

我们可以看到,它是一个需要类型参数的内部宏的别名n。因此,传递给的\__xparse_if_no_value:nT细绳

"\l_ae_makeversions_delimiter"

而不是其内容,

-NoValue-

\l_ae_makeversions_delimiter您可以通过首先使用替换其内容来解决这个问题

\exp_args:NV \IfNoValueT \l_ae_makeversions_delimiter

如图所示V,的值\l_ae_makeversions_delimiter已恢复。


发生这种情况的第二个地点是

\seq_set_split:Nnn \l_ae_version_seq { \l_ae_makeversions_delimiter }{ \l_ae_makeversions_version_list }

其中第二个括号组的内容用作分隔符,因此分隔符被设置为字符串,"\l_ae_makeversions_delimiter"这当然不是我们的意图。相反,我们希望内容\l_ae_makeversions_delimiter用作分隔符,因此我们需要一个类型参数。可以通过以下方式生成具有所需参数类型V的变体:\seq_set_split

\cs_generate_variant:Nn \seq_set_split:Nnn {NVV}

然后用作

\seq_set_split:NVV \l_ae_version_seq \l_ae_makeversions_delimiter \l_ae_makeversions_version_list

当一切如预期时。


\begin{filecontents}{silly.tex}
    This is just a test.
\end{filecontents}
\documentclass{article}
\usepackage{xparse}
\NewDocumentCommand{\setversion}{ m }
    {
            Hello I'm version \texttt{#1}.\par        
    }
\ExplSyntaxOn
\cs_generate_variant:Nn \seq_set_split:Nnn {NVV}
\NewDocumentCommand{\makeversions}{ somm }
    {
        \bool_set:Nn \l_ae_makeversions_star         { #1 }
        \tl_set:Nn   \l_ae_makeversions_delimiter    { #2 }
        \tl_set:Nn   \l_ae_makeversions_version_list { #3 }
        \tl_set:Nn   \l_ae_makeversions_input_file   { #4 }
        \exp_args:NV \IfNoValueT \l_ae_makeversions_delimiter
                    {
                        \tl_set:Nn \l_ae_makeversions_delimiter { ; }
                    }
%% In the following, I want the first commented out line of code to work.
%% Only one of the next THREE lines should be left uncommented.
        \seq_set_split:NVV \l_ae_version_seq \l_ae_makeversions_delimiter \l_ae_makeversions_version_list
        \seq_map_inline:Nn \l_ae_version_seq { 
                                                 \setversion{##1}
                                                 \input{  \l_ae_makeversions_input_file }
                                                 \IfBooleanT { \l_ae_makeversions_star } 
                                                             { \par\clearpage }
                                              }
    }


\ExplSyntaxOff
\pagestyle{empty}
\begin{document}

\makeversions{a;b;c;d}{silly}

\end{document}

答案2

你似乎毫无理由地做了很多作业。删减你的例子并“整理”,我就可以

\begin{filecontents}{silly.tex}
    This is just a test.
\end{filecontents}
\documentclass{article}
\usepackage{xparse}
\NewDocumentCommand{\setversion}{m}
    {Hello I'm version \texttt{#1}.\par}
\ExplSyntaxOn
\seq_new:N  \l_ae_version_seq
\NewDocumentCommand { \makeversions } { s O { ; } m m }
  {
    \seq_set_split:Nnn \l_ae_version_seq {#2} {#3}
    \seq_map_inline:Nn \l_ae_version_seq
      { 
        \setversion {##1}
        \input {#4}
        \IfBooleanT #1 { \par\clearpage }
      }
    }
\ExplSyntaxOff
\pagestyle{empty}
\begin{document}

\makeversions{a;b;c;d}{silly}

\end{document}

(理想情况下,\makeversions\setversion指向代码级函数,但我认为这可能会掩盖这里的观点。)

我试图掌握的关键点是什么?

  • 变量应该声明
  • O { <default> }文档级命令的可选参数具有默认值,使用(界面的一部分)而不是o加上\IfNoValue测试(代码的一部分)来“正确”处理
  • s类型返回一个布尔值,可以直接测试
  • \IfNoValue(TF)\IfBoolean(TF)旨在使用直接地根据输入做出分支决策,而不是根据存储的值

相关内容