Programmatically define macro within the body of \foreach
There are a few problems with your code. The first one, that generates the error, is that \foreach
does (something equivalent to) \def\pgffor@body{<loop code>}
, and if <loop code>
contains a #
, that will error (try it). The solution to this one is to double the #
, so
\IfNoValueF{##1}{^{##1}}
then there will be no errors... but your commands won't work either :D
The first roadblock is that \foreach
creates a group around the loop code, so local assignments are lost and, as you might have guessed by now, \NewDocumentCommand
creates the command in a local scope (prepending \global
to it won't work either), so after \foreach
ends your commands won't exist anymore. \foreach
is not a good choice to define commands (or programming tasks in general, in my opinion).
The second problem is that your commands are defined as (simplified):
\def\FF#1{\ensuremath {\mathbb {\letter }\IfNoValueF {#1}{^{#1}}}}
\def\NN#1{\ensuremath {\mathbb {\letter }\IfNoValueF {#1}{^{#1}}}}
\def\ZZ#1{\ensuremath {\mathbb {\letter }\IfNoValueF {#1}{^{#1}}}}
...
and what is \letter
? Likely H
, since it's the last value in the loop, but with recent versions of pgffor
it's undefined (or something you don't expect).
My suggestion to defeat both problems in one go is to not use \foreach
. Use instead a looping construct that doesn't do groups, and preferably allows the loop item to be #1
rather than a macro. expl3
's \clist_map_inline:nn
fits the bill:
\RequirePackage{xparse}
\ExplSyntaxOn
\clist_map_inline:nn { F,N,Z,Q,R,C,H }
{
\exp_args:Nc \NewDocumentCommand { #1#1 } { o }
{\ensuremath{\mathbb{#1}\IfNoValueF{##1}{^{##1}}}}
}
\ExplSyntaxOff
(\exp_args:Nc \macro { <tokens> }
is \expandafter\macro\csname <tokens>\endcsname
).
You don't mention it but I assume you are using the loop from pgf which places a group around each iteration. It's easier to use a different construct eg map over the list with expl3.
\documentclass{article}
\usepackage{amsfonts}
\ExplSyntaxOn
\clist_map_inline:nn{F,N,Z,Q,R,C,H}{
\expandafter\NewDocumentCommand\csname#1#1\endcsname{o}{\ensuremath{\mathbb{#1}\IfNoValueF{##1}{^{##1}}}}
}
\ExplSyntaxOff
\begin{document}
\FF \FF[x]
\QQ \QQ[2]
\ZZ \ZZ[\infty]
\end{document}
\documentclass{article}
\usepackage{amssymb,listofitems}
\def\ZZstencil#1#2\relax{%
\mathbb{#1}\ifx\relax#2\relax\else^{#2}\fi
}
\def\ZZargs{[1][]}
\newcommand\makeZZ[1]{%
\readlist\mylist{#1}%
\foreachitem\z\in\mylist[]{%
\expandafter\newcommand\csname\z\z\expandafter\expandafter
\expandafter\endcsname\expandafter\ZZargs\expandafter{%
\expandafter\ZZstencil\z####1\relax}%
}%
}
\begin{document}
\makeZZ{F,N,Z,Q,R,C,H}
$\RR$
$\RR[a+b]$
$\ZZ$
$\ZZ[(a+b)/2]$
$\HH$$\HH[x]$
\end{document}
If you really need it to \ensuremath
, then this:
\documentclass{article}
\usepackage{amssymb,listofitems}
\def\ZZstencil#1#2\relax{%
\mathbb{#1}\ifx\relax#2\relax\else^{#2}\fi
}
\def\ZZargs{[1][]}
\newcommand\makeZZ[1]{%
\readlist\mylist{#1}%
\foreachitem\z\in\mylist[]{%
\expandafter\newcommand\csname\z\z\expandafter\expandafter
\expandafter\endcsname\expandafter\ZZargs\expandafter{%
\expandafter\ensuremath\expandafter{\expandafter
\ZZstencil\z####1\relax}}%
}%
}
\begin{document}
\makeZZ{F,N,Z,Q,R,C,H}
\RR\ \RR[a+b]\ \ZZ\ \ZZ[(a+b)/2]\ $\HH\HH[x]$
\end{document}