Cheers to @egreg again for these nice expl3 tricks that finally led me to this \apply macro I use all.the.time:
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
% use: \apply[parse][separator]{function}{list}
\NewDocumentCommand{\apply}{ O{##1} O{} m +m}{%
\Apply:nnnf { #1 } { #2 } { #3 } { #4 }%
}
% The list built and processed from the input items.
\seq_new:N \l__Apply_input_seq
% The list finally reinjected in the input stream with separators.
\seq_new:N \l__Apply_output_seq
% Main function.
\cs_new_protected:Nn \Apply:nnnn
{%
% Build up input list from comma-separated items.
\seq_set_from_clist:Nn \l__Apply_input_seq { #4 }%
% Define the function to map along the parsed sequence.
\cs_set:Npn __Apply_process_multi_args:w%
#1 \q_stop % The 'arguments'
{ \exp_not:n { #3 } } % The 'body'
% Wrap the mapped function into a function that only takes 1 argument.
\cs_set:Nn __Apply_process_single_arg:n%
{ __Apply_process_multi_args:w ##1 \q_stop }%
% Map the function along the sequence.
\seq_set_map:NNn \l__Apply_output_seq \l__Apply_input_seq%
{%
% Expansion fiddling?
__Apply_process_single_arg:f { \exp_not:n { ##1 } }%
}%
% Reinject into the input stream with the requested separator.
\seq_use:Nn \l__Apply_output_seq { #2 }%
}
% Extend signature so it correctly expands items?
\cs_generate_variant:Nn __Apply_process_single_arg:n { f }%
% Extend signature so it correctly expands the input list?
\cs_generate_variant:Nn \Apply:nnnn { nnnf }%
\ExplSyntaxOff
\begin{document}
\apply[#1:#2 -> #3][, ]{%
#1 and #2 yield #3%
}{
first:last -> mid,
5:8 -> 13,
good:bad -> well,
}
\end{document}
To refine my use of \apply, I would love the [parse] argument to provide named slots, that expl3 would convert to expandable csnames within the {function} body. I a nutshell, the above call would be replaced with
\apply[\some:\other -> \result][, ]{%
\some{} and \other{} yield \result
}{
first:last -> mid,
5:8 -> 13,
good:bad -> well,
}
And why not a mix of both worlds?
\apply[#1:\other -> #2][, ]{%
#1 and \other{} yield #2%
}{
first:last -> mid,
5:8 -> 13,
good:bad -> well,
}
Is this achievable with expl3? What could I get started with?
