![LaTeX3 动态生成 \prg_new_conditional](https://linux22.com/image/465079/LaTeX3%20%E5%8A%A8%E6%80%81%E7%94%9F%E6%88%90%20%5Cprg_new_conditional.png)
我有多段冗余的 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 }
为了解决这个问题我尝试遵循这个结构:
- 创建新的 clistA
- 迭代 {p, T, F, TF}
- 对于每个元素,检查函数 + 元素是否可用
\cs_if_free
- 如果可用,将其添加到 clistB
- 使用 arg1 和 clistB 创建条件
- 使用 arg2 作为控制序列
- 尝试消除论点歧义
欢迎提出任何反馈!
答案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}