# What is the correct way to replace “csxdef/foreach” with pure expl3 code?

I'm trying to remove the dependencies of the packages pgffor and etoolbox from a code I own which uses expl3, but I don't know which is the correct equivalent of \csxdef in expl3. At the moment I have the following code, which works, but I don't know if it's the right way to replace it.

\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}


Commands take two arguments, where the second is separated by commas and is not used directly in the document, \csuse is passed as an argument to another code I have defined. I don't know if what I should use is \cs_new: or \l_#1#2_tl or if expl3 has a function that already does this (I don't want to reinvent the wheel).

Greetings.

This is more of a comment that is too long for a comment....

Since it seems that you want to be able to redefine these commands I think that you need to use \cs_set:cpx (or \cs_gset:cpx or \cs_set:cx or \cs_set:cx), but you don't need two functions here and, instead, the following is sufficient:

\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}


I have also given a "helper" command \mycmd.

You can also use \cs_set:cx or \cs_gset:cx but, as Joseph points out in the comments, \cs_set:cpx and \cs_gset:cpx are much faster.

• Thank you very much for the explanation, in fact the saying using pgffor|csxdef I took it from (tex.stackexchange.com/a/378258/7832) which is an answer from yourself. – Pablo González L Jun 17 '19 at 1:25
• The p versions are much faster ... – Joseph Wright Jun 20 '19 at 12:02
• @JosephWright Thanks for pointing this out. – Andrew Jun 20 '19 at 22:06

I'd do a little different. The code as you put it iterates through the comma list 2+<num> times, with <num> being the number of items in the list. The first iteration is in \clist_set:Nn, which loops through it trimming spaces. The second iteration is in \clist_count:N, which loops again to count the number of items. And as many iterations as there are items in the list with \clist_item:Nn, which doesn't just skip to the item you want, but scans each item until it finds the one you want. It's rather suboptimal. In fact, using l3benchmark to measure the performance, 10 thousand iterations of your code runs in 1.32 s, while the code below runs in 0.39 s, more than 3x speedup.

I suggest using \clist_map_inline:nn (or something similar), which will iterate only once through the list, and use an auxiliary counter to keep track of the item number.

I'd say that you should use a <tl var> here with \tl_new:c and \tl_set:cx rather than \cs_set:cx (which, by the way, is much slower than \cs_set:cpx). And, since the generated items aren't functions (in expl3 terminology), but variables, this alternative seems better. Then the accessor function can just be changed to be \tl_use:c, which will check if the requested variable exists and issue an error if appropriate. Using \use:c here is not desired because if the requested variable does not exist it will expand to \relax and will not raise an error.

Finally, if you plan to reuse the created token lists, then instead of \tl_new:c (which raises an error if the variable exists), you can use \tl_clear_new:c which will clear the variable, or create it, depending if it existed or not.

\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 note: \tl_set:cx does \cs_set_nopar:cpx (i.e., \expandafter\xdef\csname...\endcsname, so the \tl_new:c is redundant. But it is in the best practices to declare the variable before using it.

• Thanks for the answer, I had my doubts about the correct use of cs_new vs \tl_new, putting \tl_new:c is almost obligatory in my case, otherwise it doesn't happen when using debug_on:n {...} (which I think is good practice :). Query ... Is there a difference between use:c and \tl_use:c? – Pablo González L Jun 17 '19 at 3:33
• @PabloGonzálezL \use:c{name} is exactly \csname name\endcsname. If \name exists it will be used and if it is not TeX will implicitly \let\name\relax and use it with no error message. \tl_use:c uses \tl_use:N, which checks if the tl var exists before using it and prints and error message if it does not. So again, in the realm of best practices, \tl_use:c is a better option because it will not silently do something you don't want it to. (Sorry for the delay, I posted the answer right before going to bed and just woke up :-) – Phelype Oleinik Jun 17 '19 at 8:56
• ...Okay, I understand, one last query (if you can add to your answer). If I need to rewrite \mycmdtwo (repeat) should I occupy \use:c or should I change \tl_new:c to \tl_clear_new:c ?...Saludos – Pablo González L Jun 17 '19 at 11:26
• @PabloGonzálezL If you want to do \mycmdtwo{B}{P,Q,R} again to overwrite the token lists, then \tl_clear_new:c is the way to go. I'll add to the answer. Only in very specific situations \use:c will be the correct choice, mainly due to its implicit behaviour. – Phelype Oleinik Jun 17 '19 at 11:33
• Thank you very much for the explanation and analysis. – Pablo González L Jun 17 '19 at 20:18

Token list variables are different from functions. Your code is meant to store token lists, rather than define actions.

\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}


A benchmark of my solution compared with Phelype Oleynik's yields

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


Top is my solution, bottom is Phelype's.

• Thank you, I missed \seq_indexed_map_inline:Nn when reading the documentation. – Pablo González L Jun 20 '19 at 11:06
• Query, compared to the answer given by @Phelype Oleinik, which is more efficient? – Pablo González L Jun 20 '19 at 16:42
• @PabloGonzálezL I did the benchmark. – egreg Jun 22 '19 at 17:24
• Thank you very much...again. Saludos – Pablo González L Jun 22 '19 at 20:09