Here's an option using sequences, it could probably be made a little more compact as the code from the two helper macros is almost identical.
I'm not an expert so take my advice with a grain of salt but I would just stick with using sequences
. They require an extra line of code here and there, but the additional options that they provide are well worth it.
\documentclass{article}
\usepackage{xparse}
\usepackage{siunitx}
\ExplSyntaxOn
\NewDocumentCommand{\sumofsquares}{ m o }
{
\IfNoValueTF {#2}
% if optional is missing call this
{\sum_of_squares:n {#1}}
% if not, then call this
{\sum_of_squares:nn {#1}{#2}}
}
\cs_new_protected:Npn \sum_of_squares:n #1
{
% does what the name suggests, set a sequence from the clist
\seq_set_from_clist:Nn \l_tmpa_seq {#1}
% applies final arg to each element of second seq and stores result in first seq
\seq_set_map:NNn \l_tmpb_seq \l_tmpa_seq {(##1)^2}
% \seq_use puts the items from the seq back in the input with "+" as a separator
\sqrt{\seq_use:Nnnn \l_tmpb_seq {+}{+}{+}}
}
\cs_new_protected:Npn \sum_of_squares:nn #1 #2
{
% same as above, but with the addition of units
\seq_set_from_clist:Nn \l_tmpa_seq {#1}
\seq_set_map:NNn \l_tmpb_seq \l_tmpa_seq {(\SI{##1}{#2})^2}
\sqrt{\seq_use:Nnnn \l_tmpb_seq {+}{+}{+}}
}
\ExplSyntaxOff
\begin{document}
$\sumofsquares{2,3,-4}$\par\medskip
$\sumofsquares{2,3,-4}[m/s]$
\end{document}
Here's a slightly shorter (and less clear) example using clist
s. The same method could have been used to shorten the code above.
\documentclass{article}
\usepackage{xparse}
\usepackage{siunitx}
\ExplSyntaxOn
\NewDocumentCommand{\sumofsquares}{ m O{} }
{
\sum_of_squares:nn {#1}{#2}
}
\cs_new_protected:Npn \sum_of_squares:nn #1 #2
{
\tl_if_empty:nTF {#2}
{
\clist_set:Nn \l_tmpa_clist {#1}
\sqrt{(\clist_use:Nnnn \l_tmpa_clist {)^2+(} {)^2+(} {)^2+(} )^2 }
}
{
\clist_set:Nn \l_tmpa_clist {#1}
\sqrt{(\clist_use:Nnnn \l_tmpa_clist {\,\si{#2})^2+(} {\,\si{#2})^2+(} {\,\si{#2})^2+(} \,\si{#2})^2}
}
}
\ExplSyntaxOff
\begin{document}
$\sumofsquares{2,3,-4}$\par\medskip
$\sumofsquares{2,3,-4}[m/s]$
\end{document}
The main loop for comma separated lists in LaTeX3 is
\clist_map_inline:nn
The first argument is an explicit list, the second argument tells LaTeX what to do with each item. For instance, we want to print an enumerate
environment from the items:
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\makeenumerate}{ m }
{
\begin{enumerate}
\clist_map_inline:nn { #1 } { \item \fbox{##1} }
\end{enumerate}
}
\ExplSyntaxOff
\begin{document}
\makeenumerate{a, b ,c d, ,e }
\end{document}
I used \fbox
in order to illustrate some of the features:
spaces are stripped on either side of the items;
empty items are ignored (empty means only spaces between commas);
no expansion is performed on the item.
Note that the current item is denoted by #1
and it's literally available, which is not the case with the usual \@for
, where the current item is hidden in a macro. In the code above we have to use double ##1
because we're inside a macro definition.
A similar function is \clist_map_function:nN
, which has the advantage of being fully expandable (but only in x
full expansion, not f
). The above example would be
\NewDocumentCommand{\makeenumerate}{ m }
{
\begin{enumerate}
\clist_map_function:nN { #1 } \xyz_make_item:n
\end{enumerate}
}
\cs_new_protected:Npn \xyz_make_item:n #1
{
\item \fbox { #1 }
}
In this case the current item is passed as an argument to the indicated function.
List mappings can be broken; let's say, in the above example, we want to stop processing if an item is \stop
:
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\makeenumerate}{ m }
{
\begin{enumerate}
\xyz_make_items:n { #1 }
\end{enumerate}
}
\cs_new_protected:Npn \xyz_make_items:n #1
{
\clist_map_inline:nn { #1 }
{
\tl_if_eq:nnTF { ##1 } { \stop }
{
\clist_map_break:
}
{
\item \fbox { ##1 }
}
}
}
\ExplSyntaxOff
\begin{document}
\makeenumerate{a, b ,c d, \stop ,e }
\end{document}
Note that complex code shouldn't be used in \NewDocumentCommand
, so I defined an auxiliary function for this purpose.
One can also use
\clist_map_break:n
and the argument given to this function will be executed before breaking the mapping.
The same features apply when using
\keys_set:nn { <module> } { <comma list of key-value pairs> }
for evaluating a set of key-value pairs: leading and trailing spaces are ignored as are empty (blank) items.
If the comma separated list is stored in a macro, one can use
\clist_map_inline:Nn
\clist_map_function:NN
with the same ideas. In my opinion, it's bad programming style allowing both inputs and a variant should be defined.
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\makeenumerate}{ sm }
{
\begin{enumerate}
\IfBooleanTF{#1}
{
\clist_set:NV \l_xyz_input_clist #2
\xyz_make_items:V \l_xyz_input_clist
}
{
\xyz_make_items:n { #2 }
}
\end{enumerate}
}
\clist_new:N \l_xyz_input_clist
\cs_new_protected:Npn \xyz_make_items:n #1
{
\clist_map_inline:nn { #1 }
{
\tl_if_eq:nnTF { ##1 } { \stop }
{
\clist_map_break:
}
{
\item \fbox { ##1 }
}
}
}
\cs_generate_variant:Nn \xyz_make_items:n { V }
\ExplSyntaxOff
\begin{document}
\newcommand{\mylist}{A, B ,C,}
\makeenumerate{a, b ,c d, \stop ,e }
\makeenumerate*{\mylist}
\end{document}
Setting a variable with the contents of the macro holding the comma separated list is done because this process “normalizes” the comma separated list for better usage in \clist_map_inline:Nn
.
If other delimiters are desired, the better method is to go to sequences; use \seq_map_inline:Nn
or \seq_map_function:NN
after splitting the input into components with
\seq_set_split:Nnn \l_xyz_input_seq { ; } { #1 }
Full example:
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\makeenumerate}{ O{,} m }
{
\begin{enumerate}
\xyz_make_items:nn { #1 } { #2 }
\end{enumerate}
}
\seq_new:N \l_xyz_input_seq
\cs_new_protected:Npn \xyz_make_items:nn #1 #2
{
\seq_set_split:Nnn \l_xyz_input_seq { #1 } { #2 }
\seq_map_inline:Nn \l_xyz_input_seq
{
\tl_if_eq:nnTF { ##1 } { \stop }
{
\seq_map_break:
}
{
\item \fbox { ##1 }
}
}
}
\ExplSyntaxOff
\begin{document}
\makeenumerate[;]{a; b ;c, d; \stop ;e }
\end{document}
Also here the splitting ignores leading and trailing spaces and also empty items.
Best Answer
In LaTeX3, the preferred way to get user-level functions is
xparse
and its\NewDocumentCommand
function.If the list was not a comma-separated list, then you would have to do something like
Then
\PrintAnswer
would be performed outside the scope of the expl syntax.Here, your life is both slightly easier because you have a comma-separated list, and slightly harder because it is not given directly, but is given hidden inside a macro,
\inputfiles
. In the code below, I used\clist_map_inline:on
, which expands its clist argument once before performing the second argument for each item. Since this particular variant is not available in the kernel, we need to provide it, withAll in all, you can do for instance (I changed
\PrintAnswer
too)