10

I am trying to write a macro \RR, allowing me to type \mathbb{R} in text, with an optional parameter for an exponent. The desired functionality is shown below:

I can now write \RR\ in text. % --> produces: I can now write $\mathbb{R}$ in text.
Blah blah \RR[m+n] blah blah. % --> produces: Blah blah $\mathbb{R}^{m+n}$ blah blah

For a fixed letter, such as R, this is currently achieved with:

\NewDocumentCommand{\RR}{o}{\ensuremath{\mathbb{R}\IfNoValueF{#1}{^{#1}}}}

I would like to define similar commands for other letters. I realize the most practical way to do this is to simply write a version of the above for each additional letter. For learning purposes however, I'm curious how I might go about with with \foreach. I've tried something along the lines of

\foreach \letter in {F,N,Z,Q,R,C,H}{
    \expandafter\NewDocumentCommand\csname\letter\letter\endcsname{o}{\ensuremath{\mathbb{\letter}\IfNoValueF{#1}{^{#1}}}}
}

The above fails to create the command. Trying

I can now write \RR\ in text. % --> should produce: I can now write $\mathbb{R}$ in text.
Blah blah \RR[m+n] blah blah. % --> should produce: Blah blah $\mathbb{R}^{m+n}$ blah blah

yields the errors Illegal parameter number in definition of \pgffor@body at the point of definition (inside the foreach) and Undefined control sequence when I tried to use \RR as shown above.

At the very least, it seems like its missing something like an \expandafter or \noexand which I've seen used in \edef to control the expansion of the \ensuremath{\mathbb{\letter}\IfNoValueF{#1}{^{#1}}}. Any help would be appreciated, thanks!

9

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).

4
  • GroupThink :-). Feb 9 '21 at 0:00
  • By four seconds! :) Feb 9 '21 at 0:01
  • and you typed more Feb 9 '21 at 0:08
  • 1
    Thank you for the wonderful explanation - this is exactly what I was looking for! Feb 9 '21 at 3:22
6

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. enter image description here

\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}
7
  • If you have an older latex release you will need to add \usepackage{xparse} Feb 8 '21 at 23:59
  • Not enough zzz in this answer :( Feb 9 '21 at 0:01
  • 1
    @PhelypeOleinik good point, serious omission fixed Feb 9 '21 at 0:03
  • Much better :) Feb 9 '21 at 0:05
  • Thanks for the response, David. \clist_map_inline:nn seems super useful going forward. Feb 9 '21 at 3:27
4
\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}

enter image description here

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}
1
  • Thanks for the response, Steven. I appreciate this instructive alternative to '\clist_map_inline:nn' Feb 9 '21 at 3:32
3

I'd use expl3 for the job, but differently from other answers using it.

\documentclass{article}
\usepackage{amssymb}

\ExplSyntaxOn
\NewDocumentCommand{\makenumberset}{m}
 {
  \clist_map_inline:nn { #1 }
   {
    \cs_new_protected:cpn { ##1##1 } { \numberset { ##1 } }
   }
 }
\NewDocumentCommand{\numberset}{mo}
 {
  \ensuremath { \mathbb{#1}\IfValueT{#2}{^{#2}} }
 }
\ExplSyntaxOff

\makenumberset{F,N,Z,Q,R,C,H}

\begin{document}

In text \RR\ and \RR[m+n]; also in math $\RR$ and $\RR[m+n]$.

In text \ZZ\ and \ZZ[m+n]; also in math $\FF$ and $\FF[m+n]$.

And also \numberset{A} or \numberset{A}[m+n].

\end{document}

Note the abstract command for the occasional letter.

enter image description here

Of course I'd not use \ensuremath that serves no real purpose. It's not much of a save being able to type \RR in text, while having to remember a following backslash sometimes. With $\RR$ you don't have the problem.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.