0

I would like to modify/extend a collection of pre-existing macros with an inline code that the user provides as an argument.

Consider we have multiple variables, for instance:

\def\mon{Monday}
\def\tue{Tuesday}

and we need to define red versions of them:

\def\redmon{\textcolor{red}{\mon}}
\def\redtue{\textcolor{red}{\tue}}

We would like to have a command that does that for us, say as follows:

\newCommandForEach{mon,tue}{red#1}{\textcolor{red}{#1}}

Using expl3 I managed to code the simple variant that does not feed to the inline code the token but instead its name:

\NewDocumentCommandRaw{\newCommandForEach}{mmm}{
    \clist_set:Nn\l_tmpa_clist{#1} % Parse list
    \clist_map_inline:Nn\l_tmpa_clist{
        \cs_set:cpn{#2}{#3}
    }
}
\newCommandForEachRaw{mon,tue}{red#1}{\textcolor{red}{#1}}
% defines the following:
\newcommand{\redmon}{\textcolor{red}{mon}}
%    should be \mon -----------------^^^

If I store the inline command in a macro I can then chain-feed it the appropriate toke, but I do not know how to properly expand the necessary tokens so that consecutive calls do not re-write the previous definitions. The following calls also take an optional argument that shoudl specify an inline function that will be used as the csname of the input token list.

\NewDocumentCommand{\jpkNewCommandForEachRaw}{mmm}{
\clist_set:Nn\l_tmpa_clist{#1} % Parse list
\cs_set:Npn\jpk_NewCommandForEach_name_cs:n##1{#2}
\cs_set:Npn\jpk_NewCommandForEach_body_cs:n##1{#3}
\clist_map_inline:Nn\l_tmpa_clist{
    \tl_set:Nn\jpk_NewCommandForEach_name_tl{#2}
    \cs_set:cpn\jpk_NewCommandForEach_name_tl{
        \jpk_NewCommandForEach_body_cs:n{##1}
    }
}
}
\NewDocumentCommand{\jpkNewCommandForEach}{mmmO{##1}}{
    \cs_set:Npn\jpk_NewCommandForEach_bodyraw_cs:n##1{#3}
    \jpkNewCommandForEachRaw{#1}{#2}{
        \jpk_NewCommandForEach_bodyraw_cs:n{\tl_use:c{#4}}
    }
}
%
\jpkNewCommandForEachN{mon,tue}{red#1}{\textcolor{red}{#1}}[uncolored#1]
\jpkNewCommandForEachN{mon,tue}{blue#1}{\textcolor{blue}{#1}}[uncolored#1]

After this, the command \redmon gives a blue ``Monday'', as something has been overwritten. If expansion is applied, the non-expandable \textcolor fails to compile.

How can one create an (unexpanded) inline body function that takes an expanded argument?

3 Answers 3

0

The following does what you want, I used a group to contain the temporary definitions of the three helper macros, which might not be really necessary. Because of this I used the expandable \clist_map_function:nN instead of map_inline, but that shouldn't really be an issue (the performance difference is very small for clist-maps, would be something else for seqs, but parsing the clist into a seq if you don't need it for anything else would then again be slower).

\ExplSyntaxOn
\cs_new:Npn \__jpk_NewCommandForEach_foreach:n #1
  {
    \cs_set:Npn
    \exp_not:c { \__jpk_NewCommandForEach_new_name:n {#1} }
      {
        \exp_args:Nc \__jpk_NewCommandForEach_code:N
          { \__jpk_NewCommandForEach_rel_name:n {#1} }
      }
  }
\NewDocumentCommand \jpkNewCommandForEach { m m m O{##1} }
  {
    \group_begin:
      \cs_set:Npn \__jpk_NewCommandForEach_new_name:n ##1 {#2}
      \cs_set:Npn \__jpk_NewCommandForEach_code:N ##1 { \exp_not:n {#3} }
      \cs_set:Npn \__jpk_NewCommandForEach_rel_name:n ##1 {#4}
      \exp_last_unbraced:Ne
    \group_end:
    { \clist_map_function:nN {#1} \__jpk_NewCommandForEach_foreach:n }
  }
\ExplSyntaxOff

\newcommand*\uncoloredmon{Montag}
\newcommand*\uncoloredtue{Tuesday}

\jpkNewCommandForEach{mon,tue}{red#1}{\textcolor{red}{#1}}[uncolored#1]
\jpkNewCommandForEach{mon,tue}{blue#1}{\textcolor{blue}{#1}}[uncolored#1]

\show\redmon

Results in

> \redmon=\long macro:
->\textcolor {red}{\uncoloredmon }.
4
  • Thanks, this seems to do exactly what I wanted! Now, let's try to understnd why. a) (Why) do we need exp_not:c in ..._foreach:n? Wouldn't \cs_set:cpn work? b) What is the purpose of exp_last_unbraced? Is it an implicit expansion of the main body after \group_end: ? Is it equivalent to \group_end: \use:e{ ... }? c) In general, could you shed some light in the expansions? :) Commented Nov 2, 2023 at 14:25
  • @user3250177 there are some things in LaTeX which behave differently during e-expansion and c-expansion (those are just very minor details though), so if you're building a \csname you should try to only expand in \csname-context, since the _foreach:n is used in an e-context, I used \exp_not:c to make sure the name is really built by \csname and not at any step e-expanded (in practice the difference between the two types of expansion are rather minor, and most likely a \cs_set:cpn would work for all your usages).
    – Skillmon
    Commented Nov 3, 2023 at 11:50
  • @user3250177 since the definitions of our three helpers is kept local to the group, the \exp_last_unbraced:Ne fully expands the loop in an e-type expansion before the \group_end: runs. It is not equivalent to \group_end: \use:e {...} but to \use:e { \exp_not:N \group_end: ... } (and since \group_end: isn't expandable that is equivalent to \use:e { \group_end: ... }). The unbraced versions of \exp_... insert the result of the expansion without braces around them (unlike \exp_args:Ne, which would result in \group_end: { ... } and keep our \cs_set:Npn local to the braces).
    – Skillmon
    Commented Nov 3, 2023 at 11:53
  • @user3250177 if I do not forget about this, I'll add some explanations about the expansions to the answer, but not now...
    – Skillmon
    Commented Nov 3, 2023 at 11:54
0

I modified the input syntax a bit. Also I let it expand on definition so \redmon is

\textcolor {red}{Monday} rather than \textcolor {red}{\mon} if you need to hold the expansion as \mon that could be arranged with a few more \use:whatever but if it's not needed, this form is probably simplest.

enter image description here

\documentclass{article}
\usepackage{color}
\begin{document}

\def\mon{Monday}
\def\tue{Tuesday}

\ExplSyntaxOn
\NewDocumentCommand\newCommandForEach{mmm}{
    \clist_map_inline:nn{#1}{
        \cs_set:cpe{#2##1}{\exp_not:N#3{#2}{\use:c{##1}}}
}
}

\ExplSyntaxOff

\newCommandForEach{mon,tue}{red}{\textcolor}

%\show\redmon

\redmon

\end{document}
2
  • Thanks! This is convenient. Of course, it does not have the full desired flexibility, as it assumes that #3 is a token. Commented Oct 30, 2023 at 23:33
  • @user3250177 as I say you could drop assumptions such as that and add more complication to cope with more input forms, but (a) it helps to start simple and (b) if that's actually enough there's no need to do more. I may add another version tomorrow if no one else does. Commented Oct 30, 2023 at 23:59
0

I define \adorn with three arguments: a list of “variables” to adorn, a prefix to apply to the name and a template for the “adorned” command, where #1 stands for the value of the current variable in the loop.

\documentclass{article}
\usepackage{xcolor}

\ExplSyntaxOn

\NewDocumentCommand{\adorn}{mmm}
 {% #1 = list of parameterless commands
  % #2 = prefix
  % #3 = template
  \foo_adorn:nnn { #1 } { #2 } { #3 }
 }

\cs_new_protected:Nn \__foo_adorn_do:nn {}

\cs_new_protected:Nn \foo_adorn:nnn
 {
  \cs_set_protected:Nn \__foo_adorn_do:nn { \cs_set:cpn { #2 ##2 } { #3 } }
  \clist_map_inline:nn { #1 }
   {
    \exp_args:Ne \__foo_adorn_do:nn { \exp_not:c { ##1 } } { ##1 }
   }
 }

\ExplSyntaxOff

\def\mon{Monday}
\def\tue{Tuesday}

\adorn{mon,tue}{red}{\textcolor{red}{#1}}
  
\begin{document}

\mon, \tue

\redmon, \redtue

\texttt{\meaning\redmon}

\texttt{\meaning\redtue}

\end{document}

enter image description here

If I do instead

\adorn{mon,tue}{red}{--#1--}

the output will be

enter image description here

Note that if you change the meaning of \mon you also change automatically the meaning of \redmon.

If this is undesired, then replace \exp_not:c with \exp_not:v. With the \textcolor{red} wrapper you'd get

enter image description here

1
  • Thanks for your answer! I didn't pick this one as the answer of Skilmon is gently more flexible, but I am sure one could edit this to achieve the full result. However, your approach seems simpler, and perhaps has its own elegance. Is this due to the use of cs_new_PROTECTED? What is the purpose of protecting the new commands? Could you perhaps add a short explanation of your approach? Commented Nov 2, 2023 at 14:32

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .