2

I was trying to not write 26 redundant lines; \magic works on its own; but the loop breaks it

\usepackage{pgffor,amsfonts}
\newcommand{\magic}[1]{
  \expandafter\newcommand\csname b#1\endcsname{\mathbb{#1}}
  \expandafter\newcommand\csname c#1\endcsname{\mathcal{#1}}
  \expandafter\newcommand\csname f#1\endcsname{\mathfrak{#1}}   }
\foreach \x in {A,...,Z}{\magic{\x} }

8 Answers 8

1

With pgffor it could be made work in principle by adding an \expandafter but the definitions are local and done in a group so it would not achieve what you want. So just use a plain \loop.

\documentclass{article}
\usepackage{amsfonts}
\newcommand{\magic}[1]{%
  \expandafter\newcommand\csname b#1\endcsname{\mathbb{#1}}%
  \expandafter\newcommand\csname c#1\endcsname{\mathcal{#1}}%
  \expandafter\newcommand\csname f#1\endcsname{\mathfrak{#1}}%  
}
\newcounter{iloop}  
\setcounter{iloop}{0}%
\loop
\stepcounter{iloop}%
\edef\pft{\Alph{iloop}}%
\expandafter\magic\pft%
\ifnum\value{iloop}<26\relax
\repeat
\begin{document}

$\bB$
\end{document}

enter image description here

Needless to say that defining such macros is not a good idea if you ever plan to collaborate with others and share TeX files.

1
  • 1
    I should maybe add that I made an effort to keep the \newcommands that warn you if you overwrite an existing macro. Of course, I also removed stray spaces.
    – user194703
    Apr 20, 2020 at 23:36
2

Inside the pgffor loop, two things need to occur: you need to expand the \x before it gets to \magic; and the definitions inside \magic need to be global, to break out of the scope of the for loop.

I also removed a lot of stray spaces.

\documentclass{article}
\usepackage{pgffor,amsfonts}
\newcommand{\magic}[1]{%
  \expandafter\gdef\csname b#1\endcsname{\mathbb{#1}}%
  \expandafter\gdef\csname c#1\endcsname{\mathcal{#1}}%
  \expandafter\gdef\csname f#1\endcsname{\mathfrak{#1}}}
\begin{document}  
\foreach \x in {A,...,Z}{\expandafter\magic\expandafter{\x}}

$\bX \cB \fH$
\end{document}

enter image description here

2

not perfect, but makes it short..

\usepackage{amsfonts,dowith}
\newcommand{\magic}[1]{
  \expandafter\newcommand\csname b#1\endcsname{\mathbb{#1}}
  \expandafter\newcommand\csname c#1\endcsname{\mathcal{#1}}
  \expandafter\newcommand\csname f#1\endcsname{\mathfrak{#1}}   }
\DoWith\magic ABCDEFGHIJKLMNOPQRSTUVWXYZ\StopDoing
1

If you wish, you can accumulate calls to \magic in a scratch-macro:

\documentclass{article}
\usepackage{pgffor,amsfonts}
\newcommand\scratchmacro{}%
\newcommand\PassFirsToSecond[2]{#2{#1}}%

\newcommand{\magic}[1]{%
  \expandafter\newcommand\csname b#1\endcsname{\mathbb{#1}}%
  \expandafter\newcommand\csname c#1\endcsname{\mathcal{#1}}%
  \expandafter\newcommand\csname f#1\endcsname{\mathfrak{#1}}}%

\def\scratchmacro{}%
\foreach \x in {A,...,Z}{%
  \csname g@addto@macro%
  \expandafter\endcsname
  \expandafter\scratchmacro
  \expandafter{%
    \expandafter\magic
    \expandafter{\x}%
  }%
}%
\scratchmacro

\begin{document}    
$\bX \cB \fH$
\end{document}

In this very special case you can play the \aftergroup-game—this only works as long as \x does yield exactly one token:

\documentclass{article}
\usepackage{pgffor,amsfonts}
\newcommand{\magic}[1]{%
  \expandafter\newcommand\csname b#1\endcsname{\mathbb{#1}}%
  \expandafter\newcommand\csname c#1\endcsname{\mathcal{#1}}%
  \expandafter\newcommand\csname f#1\endcsname{\mathfrak{#1}}}%

\foreach \x in {A,...,Z}{%
  \aftergroup\aftergroup
  \aftergroup           \magic
  \aftergroup\aftergroup
  \aftergroup           {%
  \aftergroup\aftergroup
  \expandafter\aftergroup\x
  \aftergroup\aftergroup
  \aftergroup           }%
}%

\begin{document}  
$\bX \cB \fH$
\end{document}

If you don't mind typing the alphabet you can implement your own tail-recursive \magicloop—the \expandafter before \magicloop in the example below is needed to make the \fi go away before processing/expanding \magicloop and hereby "snapping" another non-delimited argument from the token-stream:

\documentclass{article}
\usepackage{amsfonts}
\newcommand{\magic}[1]{%
  \expandafter\newcommand\csname b#1\endcsname{\mathbb{#1}}%
  \expandafter\newcommand\csname c#1\endcsname{\mathcal{#1}}%
  \expandafter\newcommand\csname f#1\endcsname{\mathfrak{#1}}}%
\newcommand\magicloop[1]{%
  \ifx\relax#1\else\magic{#1}\expandafter\magicloop\fi
}%
\magicloop ABCDEFGHIJKLMNOPQRSTUVWXYZ\relax

\begin{document}
$\bX \cB \fH$
\end{document}

Or directly:

\documentclass{article}
\usepackage{amsfonts}
\newcommand\magicloop[1]{%
  \ifx\relax#1\else
    \expandafter\newcommand\csname b#1\endcsname{\mathbb{#1}}%
    \expandafter\newcommand\csname c#1\endcsname{\mathcal{#1}}%
    \expandafter\newcommand\csname f#1\endcsname{\mathfrak{#1}}%
  \expandafter\magicloop\fi
}%
\magicloop ABCDEFGHIJKLMNOPQRSTUVWXYZ\relax

\begin{document}
$\bX \cB \fH$
\end{document}
1

With expl3 and with a nice syntax:

\documentclass{article}
\usepackage{amsfonts}
\usepackage{xparse}

\ExplSyntaxOn

\NewDocumentCommand{\magic}{m}
 {
  \prop_set_from_keyval:Nn \l_tmpa_prop { #1 }
  \int_step_inline:nnn { `A } { `Z }
   {
    \prop_map_inline:Nn \l_tmpa_prop
     {
      \cs_new_protected:cpx { ####1 \char_generate:nn { ##1 } { 11 } }
       {
        \exp_not:N ####2 { \char_generate:nn { ##1 } { 11 } }
       }
     }
   }
 }

\ExplSyntaxOff

\magic{b=\mathbb,c=\mathcal,f=\mathfrak}

\begin{document}

$\bA+\cF+\fG$

\end{document}

enter image description here

We store a property list containing the properties b=\mathbb, c=\mathcal and f=\mathfrak. Then we start a loop indexed by the ASCII numbers of the uppercase letters and use the properties to define the required control sequences.

In the code ##1 stands for the current integer in the loop, ####1 for the current key and ####2 for the property (at each level we need to double the hash marks).

When ##1 is 65 (that is, the ASCII code for A), the code does

\cs_new_protected:cpx { b \char_generate:nn { 65 } { 11 } }
 {
  \exp_not:N \mathbb { \char_generate:nn { 65 } { 11 } }
 }

and similarly for c and f. This is equivalent to saying

\cs_new_protected:Npn \bA { \mathbb { A } }

Of course it's possible to do it also with pgffor; you lose the check that the commands are not yet existent.

\documentclass{article}
\usepackage{amsfonts}
\usepackage{pgffor}

\begingroup
\newcommand\makecommand[3]{%
  \expandafter\xdef\csname #1#2\endcsname{\noexpand#3{#2}}%
}
\foreach \x in {A,...,Z} {
  \makecommand{b}{\x}{\mathbb}
  \makecommand{c}{\x}{\mathcal}
  \makecommand{f}{\x}{\mathfrak}
}
\endgroup

\begin{document}

$\bA+\cF+\fG$

\end{document}
1

Due to the character-limit for answers this answer is split in two parts.
This is part 1 of this answer.
Part 2 of this answer can be found here: ⟨https://tex.stackexchange.com/a/539822/118714⟩


When putting your question on a more abstract level, then your question seems to be about iterating on a list of non-delimited macro arguments that contains an arbitrary amount of elements/arguments in order to achieve some sort of foreach-loop.

I can offer some fully expandable solutions where a marker for denoting the end of the list of arguments is not needed as the end of the list is determined by the emptiness of a macro-argument.

With all solutions actually a single macro-argument is processed iteratively which in turn consists of an arbitrary amount of non-delimited arguments.

So instead of

\DoWithEachElementOfArgumentList{⟨Argument 1⟩}{⟨Argument 2⟩}...{⟨Argument n⟩}

the pattern for arguments is:

\DoWithEachElementOfArgumentList{⟨tokens to put before each argument⟩}%
                                {⟨tokens when no (more) arguments are in list⟩}%
                                {⟨tokens to put behind each argument⟩}%
                                {% List of non-delimited macro-arguments:
                                  {⟨Argument 1⟩}%
                                  {⟨Argument 2⟩}%
                                  ...
                                  {⟨Argument n⟩}%
                                }%

This will deliver something like:

⟨tokens to put before each argument⟩{⟨Argument 1⟩}⟨tokens to put behind each argument⟩%
⟨tokens to put before each argument⟩{⟨Argument 2⟩}⟨tokens to put behind each argument⟩%
...
⟨tokens to put before each argument⟩{⟨Argument n⟩}⟨tokens to put behind each argument⟩%
⟨tokens when no (more) arguments are in list⟩

But there is one problem with macro-arguments:

Non-delimited macro-arguments can be wrapped in braces but wrapping them into braces is not necessary as long as they consist only of single tokens.
But there is one exception to that rule:
Space-tokens not wrapped into braces cannot be non-delimited arguments as TeX usually discards them when gathering a non-delimited macro-argument from the token-stream. Thus if a non-delimited macro-argument is to consist of a space-token or is to contain a leading space-token, that argument must be wrapped in braces.

The question arises how you wish space-tokens to be treated.

I suppose

\DoWithEachElementOfArgumentList{\foobar}{}{}{%
  {hello}{ }{world}{ }...
}%

should yield:

\foobar{hello}\foobar{ }\foobar{world}\foobar{ }\foobar{...

But what about:

\DoWithEachElementOfArgumentList{\foobar}{}{}{%
  {hello} {world}  ...
}%

Should that yield

\foobar{hello}\foobar{world}...

or shall it yield

\foobar{hello}\foobar{ }\foobar{world}\foobar{ }...

?

In other words:

Shall space-tokens not wrapped into braces within the list of non-delimited arguments be taken into account in the same way as if they were explicitly wrapped into braces { }? Shall such space-tokens be discarded silently?


Solution 1a—Space-tokens not wrapped into braces get silently discarded:

\documentclass{article}
\usepackage{amsfonts}

\makeatletter
%%-----------------------------------------------------------------------------
%% Paraphernalia:
%%.............................................................................
\newcommand\bracestripexchange[2]{#2#1}%
%%-----------------------------------------------------------------------------
%% Check whether argument is empty:
%% -- based on \ifempty-macro by Robert R Schneck:
%%  Newsgroup: comp.text.tex
%%  Subject: Macro puzzle: maximally general \ifempty
%%  Google-Groups-URL:
%%    <https://groups.google.com/forum/#!topic/comp.text.tex/kuOEIQIrElc>
%%.............................................................................
%% \CheckWhetherNull{<Argument which is to be checked>}%
%%                  {<Tokens to be delivered in case that
%%                    argument which is to be checked is empty>}%
%%                  {<Tokens to be delivered in case that
%%                    argument which is to be checked is not empty>}%
\long\def\CheckWhetherNull#1{%
  \romannumeral0\expandafter\@secondoftwo\string{\expandafter
  \@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
  \@secondoftwo\string}\expandafter\@firstoftwo\expandafter{\expandafter
  \@secondoftwo\string}\@firstoftwo\expandafter{} \@secondoftwo}%
  {\@firstoftwo\expandafter{} \@firstoftwo}%
}%
%%-----------------------------------------------------------------------------
%% \ActOnFirstListElement{<tokens to put before each argument>}%
%%                       {<tokens when no (more) arguments are in list>}%
%%                       {<tokens to put behind each argument>}%
%%                       {{<e_k>}{<e_(k+1)>}..{<e_n>}}%
%% yields either: 
%%   <tokens when no (more) arguments are in list>
%% or:
%%   <tokens to put before each argument>{<e_k>}<tokens to put behind each
%%   argument>{{<e_(k+1)>}..{<e_n>}}
%%
%%   (   The "list" in question is {<e_k>}{<e_(k+1)>}..{<e_n>}   )
%%.............................................................................
\@ifdefinable\KeepFirstTillSelDOM{%
  \long\def\KeepFirstTillSelDOM#1#2\SelDOM{{#1}}%
}%
\newcommand\ActOnFirstListElement[4]{%
  \expandafter\CheckWhetherNull\expandafter{\@secondoftwo#4.{}}%
  {#2}%
  {%
    \expandafter\expandafter
    \expandafter            \ExtractFirstListElementLoop
    \expandafter\bracestripexchange
    \expandafter{%
    \expandafter{%
    \@firstoftwo{}#4}}{{#4\SelDOM}{#1}{#3}}%
  }%
}%
\newcommand\ExtractFirstListElementLoop[3]{%
  \expandafter\CheckWhetherNull\expandafter{\@firstoftwo{}#1}%
  {#2#1#3}%
  {%
    \expandafter\ExtractFirstListElementLoop
    \expandafter{%
    \KeepFirstTillSelDOM#1}{#2}{#3}%
  }%
}%
%%---------------------------------------------------------------
%% Expandable Loop:
%% \DoWithEachElementOfArgumentList{<tokens to put before each argument>}%
%%                                 {<tokens when no (more) arguments are in list>}%
%%                                 {<tokens to put behind each argument>}%
%%                                 {{<e_k>}{<e_(k+1)>}..{<e_n>}}
%%
%%    If iteration is done/if list is empty: <tokens when no (more) arguments are in list>
%%    Else:
%%    <tokens to put before each argument>{<e_k>}<tokens to put behind each argument>%
%%    \DoWithEachElementOfArgumentList{<tokens to put before each argument>}%
%%                                    {<tokens when no (more) arguments are in list>}%
%%                                    {<tokens to put behind each argument>}%
%%                                    {{<e_(k+1)>}..{<e_n>}}%
%%...............................................................
\newcommand\DoWithEachElementOfArgumentList[3]{%
  \ActOnFirstListElement{#1}{#2}{#3\DoWithEachElementOfArgumentList{#1}{#2}{#3}}%
}
\makeatother
%%-----------------------------------------------------------------------------
%% Test the for-loop:
%%.............................................................................
\newcommand{\magic}[1]{%
  \expandafter\newcommand\csname b#1\endcsname{\mathbb{#1}}%
  \expandafter\newcommand\csname c#1\endcsname{\mathcal{#1}}%
  \expandafter\newcommand\csname f#1\endcsname{\mathfrak{#1}}%
}%
\DoWithEachElementOfArgumentList{\magic}{}{}{%
  ABCDEFGHIJKLMNOPQRSTUVWXYZ%
}%

\begin{document}

\ttfamily\selectfont

\noindent $\bX \cB \fH$

\vfill

\newcommand\callmacros[1]{%
  \hbox{%
    \hbox{\expandafter\string\csname b#1\endcsname: $\csname b#1\endcsname$} %
    \hbox{\expandafter\string\csname c#1\endcsname: $\csname c#1\endcsname$} %
    \hbox{\expandafter\string\csname f#1\endcsname: $\csname f#1\endcsname$}%
  }%
}%
\DoWithEachElementOfArgumentList{\callmacros}{\hbox{Done.}}{}{ABCDEFGHIJKLMNOPQRSTUVWXYZ}%

\vfill

\newcommand\TokensToPutBeforeArg[1]{%
  \string\TokensToPutBeforeArg\string{#1\string}%
}%
\DoWithEachElementOfArgumentList{\par\noindent\TokensToPutBeforeArg}%
                                {\par\noindent Done.}%
                                {(TokensToPutBehindArg)}%
                                { %<-Space-Token!
                                  {Non-Space-Element01}%<-No Space-Token
                                  {Non-Space-Element02} %<- Space-Token
                                  {Non-Space-Element03}%<-No Space-Token
                                  {Non-Space-Element04} %<- Space-Token
                                  {Non-Space-Element05}%<-No Space-Token
                                  {Non-Space-Element06}%<-No Space-Token
                                  {Non-Space-Element07}%<-No Space-Token
                                  {Non-Space-Element08}%<-No Space-Token
                                  {Non-Space-Element09}%<-No Space-Token
                                  {Non-Space-Element10}%<-No Space-Token
                                  {Non-Space-Element11}%<-No Space-Token
                                  {Non-Space-Element12} %<- Space-Token
                                }%

\vfill

\end{document}

enter image description here


Solution 1b—Space-tokens not wrapped into braces are taken into account as if they were wrapped into braces:

\documentclass{article}
\usepackage{amsfonts}

\makeatletter
%%-----------------------------------------------------------------------------
%% Paraphernalia:
%%.............................................................................
\newcommand\bracestripexchange[2]{#2#1}%
\@ifdefinable\removespace{\@firstoftwo{\def\removespace}{} {}}%
%%-----------------------------------------------------------------------------
%% Check whether argument is empty:
%% -- based on \ifempty-macro by Robert R Schneck:
%%  Newsgroup: comp.text.tex
%%  Subject: Macro puzzle: maximally general \ifempty
%%  Google-Groups-URL:
%%    <https://groups.google.com/forum/#!topic/comp.text.tex/kuOEIQIrElc>
%%.............................................................................
%% \CheckWhetherNull{<Argument which is to be checked>}%
%%                  {<Tokens to be delivered in case that
%%                    argument which is to be checked is empty>}%
%%                  {<Tokens to be delivered in case that
%%                    argument which is to be checked is not empty>}%
\newcommand\CheckWhetherNull[1]{%
  \romannumeral0\expandafter\@secondoftwo\string{\expandafter
  \@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
  \@secondoftwo\string}\expandafter\@firstoftwo\expandafter{\expandafter
  \@secondoftwo\string}\@firstoftwo\expandafter{} \@secondoftwo}%
  {\@firstoftwo\expandafter{} \@firstoftwo}%
}%
%%-----------------------------------------------------------------------------
%% Check whether brace-balanced argument starts with a space-token
%%.............................................................................
%% \CheckWhetherLeadingSpace{<Argument which is to be checked>}%
%%                          {<Tokens to be delivered in case <argument
%%                            which is to be checked>'s 1st token is a
%%                            space-token>}%
%%                          {<Tokens to be delivered in case <argument
%%                            which is to be checked>'s 1st token is not
%%                            a space-token>}%
\newcommand\CheckWhetherLeadingSpace[1]{%
  \CheckWhetherNull{#1}{\@secondoftwo}{%
  \expandafter\@secondoftwo\string{\CheckWhetherLeadingSpaceB.#1 }{}}%
}%
\@ifdefinable\CheckWhetherLeadingSpaceB{%
  \long\def\CheckWhetherLeadingSpaceB#1 {%
    \expandafter\CheckWhetherNull\expandafter{\@secondoftwo#1{}}%
    {\expandafter\expandafter\expandafter\@firstoftwo}%
    {\expandafter\expandafter\expandafter\@secondoftwo}%
    \expandafter\@secondoftwo\expandafter{\string}%
  }%
}%
%%-----------------------------------------------------------------------------
%% \ActOnFirstListElement{<tokens to put before each argument>}%
%%                       {<tokens when no (more) arguments are in list>}%
%%                       {<tokens to put behind each argument>}%
%%                       {{<e_k>}{<e_(k+1)>}..{<e_n>}}%
%% yields either: <tokens when no (more) arguments are in list>
%%            or: <tokens to put before each argument>{<e_k>}<tokens to put behind each argument>{{<e_(k+1)>}..{<e_n>}}
%%
%%   (   The "list" in question is {<e_k>}{<e_(k+1)>}..{<e_n>}   )
%%.............................................................................
\@ifdefinable\KeepFirstTillSelDOM{%
  \long\def\KeepFirstTillSelDOM#1#2\SelDOM{{#1}}%
}%
\newcommand\ActOnFirstListElement[4]{%
  \CheckWhetherNull{#4}%
  {#2}%
  {%
    \CheckWhetherLeadingSpace{#4}{%
      \expandafter\bracestripexchange
      \expandafter{%
      \expandafter{%
      \removespace#4}}{#1{ }#3}%
    }{%
      \expandafter\expandafter
      \expandafter            \ExtractFirstListElementLoop
      \expandafter\bracestripexchange
      \expandafter{%
      \expandafter{%
      \@firstoftwo{}#4}}{{#4\SelDOM}{#1}{#3}}%
    }%
  }%
}%
\newcommand\ExtractFirstListElementLoop[3]{%
  \expandafter\CheckWhetherNull\expandafter{\@firstoftwo{}#1}%
  {#2#1#3}%
  {%
    \expandafter\ExtractFirstListElementLoop
    \expandafter{%
    \KeepFirstTillSelDOM#1}{#2}{#3}%
  }%
}%
%%---------------------------------------------------------------
%% Expandable Loop:
%% \DoWithEachElementOfArgumentList{<tokens to put before each argument>}%
%%                                 {<tokens when no (more) arguments are in list>}%
%%                                 {<tokens to put behind each argument>}%
%%                                 {{<e_k>}{<e_(k+1)>}..{<e_n>}}
%%
%%    If list is empty: <tokens when no (more) arguments are in list>
%%    Else:
%%    <tokens to put before each argument>{<e_k>}<preset>%
%%    \DoWithEachElementOfArgumentList{<tokens to put before each argument>}%
%%                                    {<tokens when no (more) arguments are in list>}%
%%                                    {<tokens to put behind each argument>}
%%                                    {{<e_(k+1)>}..{<e_n>}}
%%...............................................................
\newcommand\DoWithEachElementOfArgumentList[3]{%
  \ActOnFirstListElement{#1}{#2}{#3\DoWithEachElementOfArgumentList{#1}{#2}{#3}}%
}
\makeatother
%%-----------------------------------------------------------------------------
%% Test the for-loop:
%%.............................................................................
\newcommand{\magic}[1]{%
  \expandafter\newcommand\csname b#1\endcsname{\mathbb{#1}}%
  \expandafter\newcommand\csname c#1\endcsname{\mathcal{#1}}%
  \expandafter\newcommand\csname f#1\endcsname{\mathfrak{#1}}%
}%
\DoWithEachElementOfArgumentList{\magic}{}{}{%
  ABCDEFGHIJKLMNOPQRSTUVWXYZ%
}%

\begin{document}

\ttfamily\selectfont

\noindent $\bX \cB \fH$

\vfill

\newcommand\callmacros[1]{%
  \hbox{%
    \hbox{\expandafter\string\csname b#1\endcsname: $\csname b#1\endcsname$} %
    \hbox{\expandafter\string\csname c#1\endcsname: $\csname c#1\endcsname$} %
    \hbox{\expandafter\string\csname f#1\endcsname: $\csname f#1\endcsname$}%
  }%
}%
\DoWithEachElementOfArgumentList{\callmacros}{\hbox{Done.}}{}{ABCDEFGHIJKLMNOPQRSTUVWXYZ}%

\vfill

\newcommand\TokensToPutBeforeArg[1]{%
  \string\TokensToPutBeforeArg\string{#1\string}%
}%
\DoWithEachElementOfArgumentList{\par\noindent\TokensToPutBeforeArg}%
                                {\par\noindent Done.}%
                                {(TokensToPutBehindArg)}%
                                { %<-Space-Token!
                                  {Non-Space-Element01}%<-No Space-Token
                                  {Non-Space-Element02} %<- Space-Token
                                  {Non-Space-Element03}%<-No Space-Token
                                  {Non-Space-Element04} %<- Space-Token
                                  {Non-Space-Element05}%<-No Space-Token
                                  {Non-Space-Element06}%<-No Space-Token
                                  {Non-Space-Element07}%<-No Space-Token
                                  {Non-Space-Element08}%<-No Space-Token
                                  {Non-Space-Element09}%<-No Space-Token
                                  {Non-Space-Element10}%<-No Space-Token
                                  {Non-Space-Element11}%<-No Space-Token
                                  {Non-Space-Element12} %<- Space-Token
                                }%

\vfill

\end{document}

enter image description here


Due to the character-limit for answers this answer is split in two parts.
This is part 1 of this answer.
Part 2 of this answer can be found here: ⟨https://tex.stackexchange.com/a/539822/118714⟩


0

Due to the character-limit for answers this answer is split in two parts.
This is part 2 of this answer.
Part 1 of this answer can be found here: ⟨https://tex.stackexchange.com/a/539821/118714⟩



Solution 2a—Space-tokens not wrapped into braces get silently discarded:

\documentclass{article}
\usepackage{amsfonts}

\makeatletter
%%-----------------------------------------------------------------------------
%% Paraphernalia:
%%.............................................................................
\newcommand\PassFirstToSecond[2]{#2{#1}}%
%%-----------------------------------------------------------------------------
%% Check whether argument is empty:
%% -- based on \ifempty-macro by Robert R Schneck:
%%  Newsgroup: comp.text.tex
%%  Subject: Macro puzzle: maximally general \ifempty
%%  Google-Groups-URL:
%%    <https://groups.google.com/forum/#!topic/comp.text.tex/kuOEIQIrElc>
%%.............................................................................
%% \CheckWhetherNull{<Argument which is to be checked>}%
%%                  {<Tokens to be delivered in case that
%%                    argument which is to be checked is empty>}%
%%                  {<Tokens to be delivered in case that
%%                    argument which is to be checked is not empty>}%
\newcommand\CheckWhetherNull[1]{%
  \romannumeral0\expandafter\@secondoftwo\string{\expandafter
  \@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
  \@secondoftwo\string}\expandafter\@firstoftwo\expandafter{\expandafter
  \@secondoftwo\string}\@firstoftwo\expandafter{} \@secondoftwo}%
  {\@firstoftwo\expandafter{} \@firstoftwo}%
}%
%%-----------------------------------------------------------------------------
%% Fully expandable for-loop:
%%.............................................................................
%% \DoWithEachElementOfArgumentList{<tokens to put before each argument>}%
%%                                 {<tokens when no (more) arguments are in list>}%
%%                                 {<tokens to put behind each argument>}%
%%                                 {{<e_k>}{<e_(k+1)>}..{<e_n>}}
%%
%%  yields (after two expansion-steps) :
%%
%%  <tokens to put before each argument>{<e_k>}<tokens to put behind each argument>%
%%  <tokens to put before each argument>{<e_(k+1)>}<tokens to put behind each argument>%
%%  ...
%%  <tokens to put before each argument>{<e_n>}<tokens to put behind each argument>%
%%  <tokens when no (more) arguments are in list>%
%%
\@ifdefinable\KeepOnlyFirstBeforeSeLDoM{%
  \long\def\KeepOnlyFirstBeforeSeLDoM#1#2\SeLDoM{{#1}}%
}%
\newcommand\DoWithEachElementOfArgumentList{%
  \romannumeral0\MoveElementFromList{}{ }%
}%
\newcommand\MoveElementFromList[6]{%
  \CheckWhetherNull{#1}{%
    \expandafter\CheckWhetherNull\expandafter{\@secondoftwo#6.{}}{#2#4}{%
      \expandafter\expandafter\expandafter\MoveElementFromList
      \expandafter\PassFirstToSecond
      \expandafter{\@firstoftwo{}#6}{{#6\SeLDoM}{#2}{#3}{#4}{#5}}%
    }%
  }{%
    \expandafter\CheckWhetherNull\expandafter{\@firstoftwo{}#1}%
    {\MoveElementFromList{}{#2#3#1#5}}%
    {\expandafter\MoveElementFromList
     \expandafter{\KeepOnlyFirstBeforeSeLDoM#1}{#2}%
    }{#3}{#4}{#5}{#6}%
  }%
}%
\makeatother
%%-----------------------------------------------------------------------------
%% Test the for-loop:
%%.............................................................................
\newcommand{\magic}[1]{%
  \expandafter\newcommand\csname b#1\endcsname{\mathbb{#1}}%
  \expandafter\newcommand\csname c#1\endcsname{\mathcal{#1}}%
  \expandafter\newcommand\csname f#1\endcsname{\mathfrak{#1}}%
}%
\DoWithEachElementOfArgumentList{\magic}{}{}{%
  ABCDEFGHIJKLMNOPQRSTUVWXYZ%
}%

\begin{document}

\ttfamily\selectfont

\noindent $\bX \cB \fH$

\vfill

\newcommand\callmacros[1]{%
  \hbox{%
    \hbox{\expandafter\string\csname b#1\endcsname: $\csname b#1\endcsname$} %
    \hbox{\expandafter\string\csname c#1\endcsname: $\csname c#1\endcsname$} %
    \hbox{\expandafter\string\csname f#1\endcsname: $\csname f#1\endcsname$}%
  }%
}%
\DoWithEachElementOfArgumentList{\callmacros}{\hbox{Done.}}{}{ABCDEFGHIJKLMNOPQRSTUVWXYZ}%

\vfill

\newcommand\TokensToPutBeforeArg[1]{%
  \string\TokensToPutBeforeArg\string{#1\string}%
}%
\DoWithEachElementOfArgumentList{\par\noindent\TokensToPutBeforeArg}%
                                {\par\noindent Done.}%
                                {(TokensToPutBehindArg)}%
                                { %<-Space-Token!
                                  {Non-Space-Element01}%<-No Space-Token
                                  {Non-Space-Element02} %<- Space-Token
                                  {Non-Space-Element03}%<-No Space-Token
                                  {Non-Space-Element04} %<- Space-Token
                                  {Non-Space-Element05}%<-No Space-Token
                                  {Non-Space-Element06}%<-No Space-Token
                                  {Non-Space-Element07}%<-No Space-Token
                                  {Non-Space-Element08}%<-No Space-Token
                                  {Non-Space-Element09}%<-No Space-Token
                                  {Non-Space-Element10}%<-No Space-Token
                                  {Non-Space-Element11}%<-No Space-Token
                                  {Non-Space-Element12} %<- Space-Token
                                }%

\vfill

\end{document}

enter image description here


Solution 2b—Space-tokens not wrapped into braces are taken into account as if they were wrapped into braces:

\documentclass{article}
\usepackage{amsfonts}

\makeatletter
%%-----------------------------------------------------------------------------
%% Paraphernalia:
%%.............................................................................
\newcommand\PassFirstToSecond[2]{#2{#1}}%
\@ifdefinable\removespace{\@firstoftwo{\def\removespace}{} {}}%
%%-----------------------------------------------------------------------------
%% Check whether argument is empty:
%%.............................................................................
%% \CheckWhetherNull{<Argument which is to be checked>}%
%%                  {<Tokens to be delivered in case that
%%                    argument which is to be checked is empty>}%
%%                  {<Tokens to be delivered in case that
%%                    argument which is to be checked is not empty>}%
\newcommand\CheckWhetherNull[1]{%
  \romannumeral0\expandafter\@secondoftwo\string{\expandafter
  \@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
  \@secondoftwo\string}\expandafter\@firstoftwo\expandafter{\expandafter
  \@secondoftwo\string}\@firstoftwo\expandafter{} \@secondoftwo}%
  {\@firstoftwo\expandafter{} \@firstoftwo}%
}%
%%-----------------------------------------------------------------------------
%% Check whether brace-balanced argument starts with a space-token
%%.............................................................................
%% \CheckWhetherLeadingSpace{<Argument which is to be checked>}%
%%                          {<Tokens to be delivered in case <argument
%%                            which is to be checked>'s 1st token is a
%%                            space-token>}%
%%                          {<Tokens to be delivered in case <argument
%%                            which is to be checked>'s 1st token is not
%%                            a space-token>}%
\newcommand\CheckWhetherLeadingSpace[1]{%
  \CheckWhetherNull{#1}{\@secondoftwo}{%
  \expandafter\@secondoftwo\string{\CheckWhetherLeadingSpaceB.#1 }{}}%
}%
\@ifdefinable\CheckWhetherLeadingSpaceB{%
  \long\def\CheckWhetherLeadingSpaceB#1 {%
    \expandafter\CheckWhetherNull\expandafter{\@secondoftwo#1{}}%
    {\expandafter\expandafter\expandafter\@firstoftwo}%
    {\expandafter\expandafter\expandafter\@secondoftwo}%
    \expandafter\@secondoftwo\expandafter{\string}%
  }%
}%
%%-----------------------------------------------------------------------------
%% Fully expandable for-loop:
%%.............................................................................
%% \DoWithEachElementOfArgumentList{<tokens to put before each argument>}%
%%                                 {<tokens when no (more) arguments are in list>}%
%%                                 {<tokens to put behind each argument>}%
%%                                 {{<e_k>}{<e_(k+1)>}..{<e_n>}}
%%
%%  yields (after two expansion-steps) :
%%
%%  <tokens to put before each argument>{<e_k>}<tokens to put behind each argument>%
%%  <tokens to put before each argument>{<e_(k+1)>}<tokens to put behind each argument>%
%%  ...
%%  <tokens to put before each argument>{<e_n>}<tokens to put behind each argument>%
%%  <tokens when no (more) arguments are in list>%
%%
\@ifdefinable\KeepOnlyFirstBeforeSeLDoM{%
  \long\def\KeepOnlyFirstBeforeSeLDoM#1#2\SeLDoM{{#1}}%
}%
\newcommand\DoWithEachElementOfArgumentList{%
  \romannumeral0\MoveElementFromList{}{ }%
}%
\newcommand\MoveElementFromList[6]{%
  \CheckWhetherNull{#1}{%
    \CheckWhetherNull{#6}{#2#4}{%
      \CheckWhetherLeadingSpace{#6}{%
        \expandafter\PassFirstToSecond
        \expandafter{\removespace#6}{\MoveElementFromList{}{#2#3{ }#5}{#3}{#4}{#5}}%
      }{%
        \expandafter\expandafter\expandafter\MoveElementFromList
        \expandafter\PassFirstToSecond
        \expandafter{\@firstoftwo{}#6}{{#6\SeLDoM}{#2}{#3}{#4}{#5}}%
      }%
    }%
  }{%
    \expandafter\CheckWhetherNull\expandafter{\@firstoftwo{}#1}%
    {\MoveElementFromList{}{#2#3#1#5}}%
    {\expandafter\MoveElementFromList
     \expandafter{\KeepOnlyFirstBeforeSeLDoM#1}{#2}%
    }{#3}{#4}{#5}{#6}%
  }%
}%
\makeatother
%%-----------------------------------------------------------------------------
%% Test the for-loop:
%%.............................................................................
\newcommand{\magic}[1]{%
  \expandafter\newcommand\csname b#1\endcsname{\mathbb{#1}}%
  \expandafter\newcommand\csname c#1\endcsname{\mathcal{#1}}%
  \expandafter\newcommand\csname f#1\endcsname{\mathfrak{#1}}%
}%
\DoWithEachElementOfArgumentList{\magic}{}{}{%
  ABCDEFGHIJKLMNOPQRSTUVWXYZ%
}%

\begin{document}

\ttfamily\selectfont

\noindent $\bX \cB \fH$

\vfill

\newcommand\callmacros[1]{%
  \hbox{%
    \hbox{\expandafter\string\csname b#1\endcsname: $\csname b#1\endcsname$} %
    \hbox{\expandafter\string\csname c#1\endcsname: $\csname c#1\endcsname$} %
    \hbox{\expandafter\string\csname f#1\endcsname: $\csname f#1\endcsname$}%
  }%
}%
\DoWithEachElementOfArgumentList{\callmacros}{\hbox{Done.}}{}{ABCDEFGHIJKLMNOPQRSTUVWXYZ}%

\vfill

\newcommand\TokensToPutBeforeArg[1]{%
  \string\TokensToPutBeforeArg\string{#1\string}%
}%
\DoWithEachElementOfArgumentList{\par\noindent\TokensToPutBeforeArg}%
                                {\par\noindent Done.}%
                                {(TokensToPutBehindArg)}%
                                { %<-Space-Token!
                                  {Non-Space-Element01}%<-No Space-Token
                                  {Non-Space-Element02} %<- Space-Token
                                  {Non-Space-Element03}%<-No Space-Token
                                  {Non-Space-Element04} %<- Space-Token
                                  {Non-Space-Element05}%<-No Space-Token
                                  {Non-Space-Element06}%<-No Space-Token
                                  {Non-Space-Element07}%<-No Space-Token
                                  {Non-Space-Element08}%<-No Space-Token
                                  {Non-Space-Element09}%<-No Space-Token
                                  {Non-Space-Element10}%<-No Space-Token
                                  {Non-Space-Element11}%<-No Space-Token
                                  {Non-Space-Element12} %<- Space-Token
                                }%

\vfill

\end{document}

enter image description here


Difference between the a-solutions and the b-solutions:

With the a-solutions (i.e., with solution 1a and solution 2a) space-tokens within the list of non-delimited macro-arguments that are not explicitly wrapped into braces will not be taken into account while with the b-solutions (i.e., with solution 1b and solution 2b) these will be taken into account as if they were wrapped into braces.


Difference between the 1-solutions and the 2-solutions:

Above it was said that with all solutions you can do

\DoWithEachElementOfArgumentList{⟨tokens to put before each argument⟩}%
                                {⟨tokens when no (more) arguments are in list⟩}%
                                {⟨tokens to put behind each argument⟩}%
                                {% List of non-delimited macro-arguments:
                                  {⟨Argument 1⟩}%
                                  {⟨Argument 2⟩}%
                                  ...
                                  {⟨Argument n⟩}%
                                }%

and that this will deliver something like

⟨tokens to put before each argument⟩{⟨Argument 1⟩}⟨tokens to put behind each argument⟩%
⟨tokens to put before each argument⟩{⟨Argument 2⟩}⟨tokens to put behind each argument⟩%
...
⟨tokens to put before each argument⟩{⟨Argument n⟩}⟨tokens to put behind each argument⟩%
⟨tokens when no (more) arguments are in list⟩

The gist of the difference between solutions 1a/b and solutions 2a/b is about the order in time in which the processes

  • of splicing the next element from the ⟨list of non-delimited macro-arguments⟩ and forming a ⟨tokens to put before each argument⟩{⟨Argument k⟩}⟨tokens to put behind each argument⟩-instance
  • of carrying out the ⟨tokens to put before each argument⟩{⟨Argument k⟩}⟨tokens to put behind each argument⟩-instances

take place:

  • With solutions 1a/b the tokens that form the next call to \DoWithEachElementOfArgumentList are inserted into the token-stream right behind the tokens that form an ⟨tokens to put before each argument⟩{⟨Argument k⟩}⟨tokens to put behind each argument⟩-instance.

    I.e., in the first iteration you get:

    ⟨tokens to put before each argument⟩{⟨Argument 1⟩}⟨tokens to put behind each argument⟩%
    \DoWithEachElementOfArgumentList{⟨tokens to put before each argument⟩}%
                                    {⟨tokens when no (more) arguments are in list⟩}%
                                    {⟨tokens to put behind each argument⟩}%
                                    {% (remaining) list of non-delimited macro-arguments:
                                      {⟨Argument 2⟩}%
                                      ...
                                      {⟨Argument n⟩}%
                                    }%

    With solutions 1a/b the ⟨tokens to put before each argument⟩ and/or the ⟨tokens to put behind each argument⟩ can also be used for affecting the way in which subsequent ⟨tokens to put before each argument⟩{⟨Argument k⟩}⟨tokens to put behind each argument⟩-instances will be created:

    With solutions 1a/b ⟨tokens to put before each argument⟩ and/or ⟨tokens to put behind each argument⟩ can contain a macros for changing/replacing/removing those arguments/tokens that belong to the following instance(s) of the \DoWithEachElementOfArgumentList-macro.

    Thus you can, e.g., use the ⟨tokens to put before each argument⟩-argument and/or the ⟨tokens to put behind each argument⟩-argument of \DoWithEachElementOfArgumentList in order to under some circumstances change the arguments of \DoWithEachElementOfArgumentList in the following iteration-step(s).

    Thus you can, e.g., use macros within the ⟨tokens to put before each argument⟩-argument and/or within the ⟨tokens to put behind each argument⟩-argument for successively changing within each iteration-step the ⟨tokens when no (more) arguments are in list⟩.

    Thus you can also use the ⟨tokens to put before each argument⟩-argument and/or the ⟨tokens to put behind each argument⟩-argument to under some circumstances have those tokens gobbled that form the subsequent \DoWithEachElementOfArgumentList-iteration-step. This way iteration can be terminated prematurely, i.e., before the entire list of arguments was processed.

  • With solutions 2a/b splitting the list is done completely before a sequence of ⟨tokens to put before each argument⟩{⟨Argument k⟩}⟨tokens to put behind each argument⟩-instances is delivered. Due to \romannumeral0-expansion splitting and delivering requires two expansion-steps/two "hits" by \expandafter.

    I.e., after two expansion-steps/after two "hits" by \expandafter you get at once:

    ⟨tokens to put before each argument⟩{⟨Argument 1⟩}⟨tokens to put behind each argument⟩%
    ⟨tokens to put before each argument⟩{⟨Argument 2⟩}⟨tokens to put behind each argument⟩%
    ...
    ⟨tokens to put before each argument⟩{⟨Argument n⟩}⟨tokens to put behind each argument⟩%
    ⟨tokens when no (more) arguments are in list⟩

    With solutions 2a/b \romannumeral0-expansion takes place for tail-recursively collecting all ⟨tokens to put before each argument⟩{⟨Argument k⟩}⟨tokens to put behind each argument⟩-instances and the trailing ⟨tokens when no (more) arguments are in list⟩ within a macro-argument. When iteration/tail-recursion is done, all the collected instances will be delivered at once.

    With solutions 2a/b both the ⟨tokens to put before each argument⟩-argument and the ⟨tokens to put behind each argument⟩-argument cannot be used for affecting the generation of subsequent ⟨tokens to put before each argument⟩{⟨Argument k⟩}⟨tokens to put behind each argument⟩-instances:
    Any ⟨tokens to put before each argument⟩{⟨Argument k⟩}⟨tokens to put behind each argument⟩-instance will be expanded/will be carried out when the process of collecting/creating ⟨tokens to put before each argument⟩{⟨Argument k⟩}⟨tokens to put behind each argument⟩-instances is already terminated.


Due to the character-limit for answers this answer is split in two parts.
This is part 2 of this answer.
Part 1 of this answer can be found here: ⟨https://tex.stackexchange.com/a/539821/118714⟩


0

A simple solution using \pgfkeys:

\documentclass{article}
\usepackage{amsfonts}
\usepackage{pgffor}
\pgfkeys{
  @magic/.code={
    \expandafter\newcommand\csname b#1\endcsname{\mathbb{#1}}%
    \expandafter\newcommand\csname c#1\endcsname{\mathcal{#1}}%
    \expandafter\newcommand\csname f#1\endcsname{\mathfrak{#1}}%  
  },
  @magic/.list={A,...,Z},
}
\begin{document}
$\bB \cF \fZ$
\end{document}

You must log in to answer this question.

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