用纯 expl3 代码替换“csxdef/foreach”的正确方法是什么?

用纯 expl3 代码替换“csxdef/foreach”的正确方法是什么?

我正在尝试从我自己使用的代码中删除软件包pgffor和的依赖项,但我不知道哪个是中的正确等价物。目前我有以下代码,它可以工作,但我不知道这是否是替换它的正确方法。etoolboxexpl3\csxdefexpl3

\documentclass{article}
\usepackage{xparse}
\usepackage{etoolbox}
\usepackage{pgffor}
\ExplSyntaxOn
\NewDocumentCommand{\mycmdone}{ m m }
  {
   \_csxdef_pgfetoolbox:nn { #1 } { #2 }
  }

\cs_new_protected_nopar:Npn \_csxdef_pgfetoolbox:nn #1 #2
  {
  \foreach \x [count=\n] in { #2 } { \csxdef{#1\n}{\x} }  % save in \#1<n>
  }

\NewDocumentCommand{\mycmdtwo}{ m m }
  {
    \_csxdef_expl:nn { #1 } { #2 }
  }

\cs_new_protected_nopar:Npn \_csxdef_expl:nn #1 #2 
  {
    \clist_set:Nn \l_tmpa_clist {#2}
    \int_step_inline:nn { \clist_count:N \l_tmpa_clist } 
      {
        \cs_set:cpx {l_#1##1:} {  \clist_item:Nn \l_tmpa_clist { ##1 }  }
      }   
  }
\ExplSyntaxOff
\begin{document}
Test mycmdone
\mycmdone{A}{X,Y,Z,W}
\csuse{A1} \csuse{A2} \csuse{A3} \csuse{A4}
% repeat
\mycmdone{A}{X,Y,Z,W}
\csuse{A1} \csuse{A2} \csuse{A3} \csuse{A4}
\par

Test mycmdtwo
\mycmdtwo{B}{P,Q,R}
\ExplSyntaxOn
\use:c{l_B1:} ~ \use:c{l_B2:} ~ \use:c{l_B3:}
\ExplSyntaxOff
% repeat
Test mycmdtwo
\mycmdtwo{B}{P,Q,R}
\ExplSyntaxOn
\use:c{l_B1:} ~ \use:c{l_B2:} ~ \use:c{l_B3:}
\ExplSyntaxOff
\end{document}

命令接受两个参数,其中第二个参数用逗号分隔,不直接在文档中使用,而是\csuse作为参数传递给我定义的另一个代码。我不知道我应该使用什么\cs_new:,或者\l_#1#2_tl是否expl3有一个已经执行此操作的函数(我不想重新发明轮子)。

问候。

答案1

这更像是一条对于评论来说太长的评论......

因为看起来您希望能够重新定义这些命令,所以我认为您需要使用\cs_set:cpx(或\cs_gset:cpx\cs_set:cx\cs_set:cx),但这里不需要两个函数,而是以下内容就足够了:

\documentclass{article}
\usepackage{xparse}
\usepackage{etoolbox}
\usepackage{pgffor}
\ExplSyntaxOn
\NewDocumentCommand{\mycmdtwo}{ m m }
  {
    \clist_set:Nn \l_tmpa_clist {#2}
    \int_step_inline:nn {\clist_count:N \l_tmpa_clist}
    {
      \cs_set:cpx {l_#1##1:} { \clist_item:Nn \l_tmpa_clist {##1} }
    }
  }
\NewDocumentCommand\mycmd{m}{\use:c{l_#1:}}
\ExplSyntaxOff
\begin{document}

Test mycmdtwo
\mycmdtwo{B}{P,Q,R}
\mycmd{B1} \mycmd{B2} \mycmd{B3}

% repeat
Test mycmdtwo
\mycmdtwo{B}{P,Q,R}
\mycmd{B1} \mycmd{B2} \mycmd{B3}

\end{document}

我还给出了“帮助”命令\mycmd

您也可以使用\cs_set:cx\cs_gset:cx但是,正如约瑟夫在评论中指出的那样,\cs_set:cpx而且\cs_gset:cpx速度要快得多。

答案2

我会做一点不同的事情。您所放置的代码会遍历逗号列表2+<num>次,其中<num>是列表中的项目数。第一次迭代是在\clist_set:Nn,它循环修剪空格。第二次迭代是在\clist_count:N,它再次循环以计算项目数。并且迭代次数与列表中的项目数一样多\clist_item:Nn,它不会直接跳到您想要的项目,而是扫描每个项目直到找到您想要的项目。这不是最理想的。事实上,用来l3benchmark衡量性能,您的代码的 10 万次迭代运行在 1.32 秒内,而下面的代码运行在 0.39 秒内,速度提高了 3 倍多。

我建议使用\clist_map_inline:nn(或类似的东西),它将只在列表中迭代一次,并使用辅助计数器来跟踪项目编号。

我认为您应该<tl var>在这里使用 和\tl_new:c\tl_set:cx不是\cs_set:cx(顺便说一句,它比 慢得多\cs_set:cpx)。而且,由于生成的项不是函数(expl3术语上),而是变量,因此这种替代方法似乎更好。然后可以将访问器函数更改为\tl_use:c,它将检查请求的变量是否存在并在适当时发出错误。\use:c不希望在这里使用,因为如果请求的变量不存在,它将扩展为\relax并且不会引发错误。

最后,如果您计划重复使用已创建的标记列表,那么您可以使用清除变量或创建变量(取决于\tl_new:c变量是否存在)来代替(如果变量存在则会引发错误) 。\tl_clear_new:c

\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\int_new:N \l_pablo_tmp_int
\NewDocumentCommand{\mycmdtwo}{ m m }
  {
    \int_zero:N \l_pablo_tmp_int
    \clist_map_inline:nn {#2}
      {
        \int_incr:N \l_pablo_tmp_int
        \tl_clear_new:c { l_pablo_#1 \int_use:N \l_pablo_tmp_int _tl } % Redundant here, but good practice in general
        \tl_set:cx { l_pablo_#1 \int_use:N \l_pablo_tmp_int _tl } {##1}
      }
  }
\NewDocumentCommand\mycmd {m} { \tl_use:c { l_pablo_#1_tl } }
\ExplSyntaxOff
\begin{document}
Test mycmdtwo
\mycmdtwo{B}{P,Q,R}
\mycmd{B1} \mycmd{B2} \mycmd{B3}

% repeat
Test mycmdtwo
\mycmdtwo{B}{P,Q,R}
\mycmd{B1} \mycmd{B2} \mycmd{B3}
\end{document}

TeXhackers 注释: \tl_set:cx\cs_set_nopar:cpxIE, \expandafter\xdef\csname...\endcsname,所以\tl_new:c是多余的。但最佳实践是先声明变量再使用它。

答案3

标记列表变量与函数不同。您的代码旨在存储标记列表,而不是定义操作。

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\mycmd}{mm}
 {
  \seq_set_from_clist:Nn \l__pablo_mycmd_seq { #2 }
  \seq_indexed_map_inline:Nn \l__pablo_mycmd_seq
   {
    \tl_clear_new:c { l_pablo_#1##1_tl }
    \tl_set:cn { l_pablo_#1##1_tl } { ##2 }
   }
 }
\NewExpandableDocumentCommand{\pablouse}{m}
 {
  \tl_use:c { l_pablo_#1_tl }
 }
\ExplSyntaxOff

\begin{document}

Test mycmd
\mycmd{A}{X,Y,Z,W}

\pablouse{A1} \pablouse{A2} \pablouse{A3} \pablouse{A4}

\end{document}

我的解决方案与 Phelype Oleynik 的收益的比较基准

8.22e-5 seconds (252 ops)
1.08e-4 seconds (321 ops)

上面是我的解决方案,下面是 Phelype 的。

相关内容