LaTeX3 动态生成 \prg_new_conditional

LaTeX3 动态生成 \prg_new_conditional

我有多段冗余的 LaTeX3 代码,它们会创建新的条件:

\bool_if:nT
  { \cs_if_free_p:N \mypkg_if_package_loaded_p:n &&
    \cs_if_free_p:N \mypkg_if_package_loaded_T:n &&
    \cs_if_free_p:N \mypkg_if_package_loaded_F:n &&
    \cs_if_free_p:N \mypkg_if_package_loaded_TF:n }
  {
    \prg_new_conditional:Nnn \mypkg_if_package_loaded:n { p, T, F, TF }
      {
        \use:c { @ifpackageloaded } { #1 }
          { \prg_return_true: }
          { \prg_return_false: }
      }
  }

我想用更简洁的代码替换第一个代码,如下所示:

\cs_new_protected:Npn \mypkg_def_if:Nn #1#2
  {
    \clist_new:N \l_mypkg_cond_clist
    \clist_map_inline:nn { p, T, F, TF }
      {
        \bool_if:nT { \cs_if_free_p:N #1:##1 }
          { \clist_put_right:Nn \l_mypkg_cond_clist { #1 } }
      }
    \prg_new_conditional:Nnn #2:n { \l_mypkg_cond_clist }
      {
        \use:c { #2 } { ##1 }
          { \prg_return_true: }
          { \prg_return_false: }
      }
  }

\mypkg_def_if:Nn \mypkg_if_package_loaded { @ifpackageloaded }
\mypkg_def_if:Nn \mypkg_if_package_with { @ifpackagewith }
\mypkg_def_if:Nn \mypkg_if_class_loaded { @ifclassloaded }
\mypkg_def_if:Nn \mypkg_if_class_with { @ifclasswith }

为了解决这个问题我尝试遵循这个结构:

  1. 创建新的 clistA
  2. 迭代 {p, T, F, TF}
  3. 对于每个元素,检查函数 + 元素是否可用\cs_if_free
  4. 如果可用,将其添加到 clistB
  5. 使用 arg1 和 clistB 创建条件
  6. 使用 arg2 作为控制序列
  7. 尝试消除论点歧义

欢迎提出任何反馈!

答案1

我看不出有什么必要\cs_if_free:NTF。你正在开发一个包,如果你不小心重新定义一个已经定义的条件在您自己的命名空间中,您将收到一条错误消息。在这种情况下,只需删除有问题的代码并继续。

这就是为什么建议使用特定于包的前缀的原因:您,作为包作者,对使用该前缀的函数和变量负全部责任。

实际上,你用你的策略\bool_if:nT把问题扫到了地毯下。假设你已经\mypkg_if_foo:nTF在你的大包中定义了,但后来你忘记了这样做,并认为这foo对其他条件来说是好的。如果你使用你的\bool_if:nT策略,你的新条件将不是被定义,并且每次调用时仍将使用旧函数\mypkg_if_foo:nTF。你确定你不希望出现错误消息,以便你能够意识到错误并选择新名称吗?我会的。

在“false”分支中使用\bool_if:nTF警告会有所帮助,但我认为没有任何好处。

最后,但并非最不重要的一点是,类似函数的定义不应分散在包中,而应保持彼此靠近,以便于维护。因此,您将有一个部分

% define conditionals emulating the LaTeX kernel pseudoconditionals
\prg_new_conditional:Nnn \mypkg_if_package_loaded:n { p, T, F, TF }
 {
  \use:c { @ifpackageloaded } { #1 } { \prg_return_true: } { \prg_return_false: }
 }

现在我们可以编写语法糖来定义这种条件“立即”。

% define conditionals emulating the LaTeX kernel pseudoconditionals
\cs_new_protected:Nn \__mypkg_define_kernel_conditional:Nn
 {
  \prg_new_conditional:Nnn #1 { p,T,F,TF }
   {
    \use:c { #2 } { \prg_return_true: } { \prg_return_false: }
   }
 }
\__mypkg_define_kernel_conditional:Nn \mypkg_if_package_loaded:n { @ifpackageloaded }
\__mypkg_define_kernel_conditional:Nn \mypkg_if_package_with:n { @ifpackagewith }
\__mypkg_define_kernel_conditional:Nn \mypkg_if_class_loaded:n { @ifclassloaded }
\__mypkg_define_kernel_conditional:Nn \mypkg_if_class_with:n { @ifclasswith }

如果你真的想使用你的\bool_if:nT策略,那么

\cs_new_protected:Nn \__mypkg_define_kernel_conditional:nn
 {
  \bool_if:nT
   {
    \cs_if_free_p:c {mypkg_if_#1_p:n} &&
    \cs_if_free_p:c {mypkg_if_#1:nT &&
    \cs_if_free_p:c {mypkg_if_#1:nF &&
    \cs_if_free_p:c {mypkg_if_#1:nTF
   }
   {
    \prg_new_conditional:cnn {mypkg_if_#1:n} { p,T,F,TF }
     {
      \use:c { #2 } { \prg_return_true: } { \prg_return_false: }
     }
   }
 }
\__mypkg_define_kernel_conditional:nn {package_loaded} { @ifpackageloaded }
\__mypkg_define_kernel_conditional:nn {package_with} { @ifpackagewith }
\__mypkg_define_kernel_conditional:nn {class_loaded} { @ifclassloaded }
\__mypkg_define_kernel_conditional:nn {class_with} { @ifclasswith }

答案2

我仍然认为您的包是您的命名空间,因此您有责任。只要不通过用户界面使用它来设置一些内部辅助程序,我不建议使用它,而是简单地执行\cs_new_eq:Nc \nevada_if_package_loaded:nTF { @ifpackageloaded }以获取条件,如果您确实需要 2e 宏的包名称。

以下内容可能比它应该的更复杂,但是可以满足您的要求:

\documentclass{article}

\ExplSyntaxOn
\clist_new:N \l__nevada_variants_clist
\tl_new:N \l__nevada_cs_parts_tl
\tl_new:N \l__nevada_arg_forwarded_tl
\str_new:N \l__nevada_cs_base_str
\str_new:N \l__nevada_cs_args_str
\int_new:N \l__nevada_arg_count_int
\msg_new:nnn { nevada } { missing-colon }
  { Function~ name~ #1~ misses~ a~ colon. }
\cs_new_protected:Npn \__nevada_split_cs:N #1
  {
    \tl_set:Nx \l__nevada_cs_parts_tl { \cs_split_function:N #1 }
    \bool_if:nF { \exp_last_unbraced:No \use_iii:nnn \l__nevada_cs_parts_tl }
      {
        \msg_error:nnn { nevada } { missing-colon } {#1}
        \prg_break:
      }
    \str_set:Nx \l__nevada_cs_base_str
      { \exp_last_unbraced:No \use_i:nnn  \l__nevada_cs_parts_tl }
    \str_set:Nx \l__nevada_cs_args_str
      { \exp_last_unbraced:No \use_ii:nnn \l__nevada_cs_parts_tl }
  }
\cs_new:Npn \__nevada_cs_base:
  { \exp_last_unbraced:No \use_i:nnn \l__nevada_cs_parts_tl }
\cs_new_protected:Npn \__nevada_provide_conditional:NNnn #1#2#3#4
  {
    \__nevada_split_cs:N #2
    \clist_clear:N \l__nevada_variants_clist
    \clist_map_inline:nn {#3}
      {
        \cs_if_exist:cF
          {
            \l__nevada_cs_base_str
            \str_if_eq:nnT {##1} { p } { _p }
            : \l__nevada_cs_args_str
            \str_if_eq:nnF {##1} { p } { \tl_to_str:n {##1} }
          }
          { \clist_put_right:Nn \l__nevada_variants_clist {##1} }
      }
    \clist_if_empty:NF \l__nevada_variants_clist
      {
        \exp_args:NNe #1 #2
          { \clist_use:Nn \l__nevada_variants_clist { , } }
          {#4}
      }
    \prg_break_point:
  }
\cs_new_protected:Npn \nevada_provide_conditional:Nnn
  { \__nevada_provide_conditional:NNnn \prg_new_conditional:Nnn }
\cs_new_protected:Npn \nevada_provide_protected_conditional:Nnn
  { \__nevada_provide_conditional:NNnn \prg_new_protected_conditional:Nnn }

\cs_new_protected:Npn \nevada_provide_all_conditionals:Nn #1
  { \nevada_provide_conditional:Nnn #1 { TF, T, F, p } }
\cs_new_protected:Npn \nevada_provide_all_protected_conditionals:Nn #1
  { \nevada_provide_protected_conditional:Nnn #1 { TF, T, F } }
\cs_new_protected:Npn \__nevada_provide_conditional_alias:NNN #1#2#3
  {
    \__nevada_split_cs:N #2
    \int_set:Nn \l__nevada_arg_count_int { \str_count:N \l__nevada_cs_args_str }
    \tl_clear:N \l__nevada_arg_forwarded_tl
    \int_step_inline:nn { \l__nevada_arg_count_int }
      { \tl_put_right:Nn \l__nevada_arg_forwarded_tl { {######1} } }
    \exp_args:NNe #1 #2
      {
        \exp_not:N #3 \l__nevada_arg_forwarded_tl
          \exp_not:N \prg_return_true:
          \exp_not:N \prg_return_false:
      }
    \prg_break_point:
  }
\cs_new_protected:Npn \nevada_provide_conditional_alias:NN
  {
    \__nevada_provide_conditional_alias:NNN \nevada_provide_all_conditionals:Nn
  }
\cs_new_protected:Npn \nevada_provide_protected_conditional_alias:NN
  {
    \__nevada_provide_conditional_alias:NNN
      \nevada_provide_all_protected_conditionals:Nn
  }
\cs_generate_variant:Nn \nevada_provide_protected_conditional_alias:NN { Nc }

\nevada_provide_protected_conditional_alias:Nc
  \nevada_if_package_loaded:n
  { @ifpackageloaded }

\ExplSyntaxOff

\usepackage{graphicx}

\begin{document}
\texttt{graphicx} is
\ExplSyntaxOn
\nevada_if_package_loaded:nF {graphicx} { not~ }
\ExplSyntaxOff
loaded.

You
\ExplSyntaxOn
\nevada_if_package_loaded:nTF {xcolor} { did } { should } ~
\ExplSyntaxOff
load \texttt{xcolor}.
\end{document}

相关内容