7

I am using custom commands to facilitate both writing and reading LaTeX equations, such as :

\newcommand{\bbA}{\mathbb{A}}
\newcommand{\bbB}{\mathbb{B}}
\newcommand{\bbC}{\mathbb{C}}
% etc...
\newcommand{\mcA}{\mathcal{A}}
\newcommand{\mcB}{\mathcal{B}}
\newcommand{\mcC}{\mathcal{C}}
% etc...
\newcommand{\msA}{\mathscr{A}}
\newcommand{\msB}{\mathscr{B}}
\newcommand{\msC}{\mathscr{C}}
% etc...
\newcommand{\mfA}{\mathfrak{A}}
\newcommand{\mfB}{\mathfrak{B}}
\newcommand{\mfC}{\mathfrak{C}}
% etc...
\newcommand{\mfa}{\mathfrak{a}}
\newcommand{\mfb}{\mathfrak{b}}
\newcommand{\mfc}{\mathfrak{c}}
% etc...

However, this takes up 130 lines of code in the file header (that I, fortunately, didn't write by hand, I made a program to do so, which is why I'm quite positive there's a solution to my problem)...

TeXStudio allows me to enclose it between %BEGIN_FOLD and %END_FOLD comments, that make me able to collapse this whole mess. However, I have two concerns :

  1. The %BEGIN_FOLD and %END_FOLD thing doesn't collapse by default, it gets expanded back upon re-opening the file. Moreover, I'm not sure this isn't a TeXStudio-only feature.

  2. Does it affect compiling time ? I don't know how the compiler handles \newcommands, but if it has to re-define and re-compile them all every time, I'm sure there's room for improvement.

Thus, I am asking the following :

Is there a way to compact each block of 26 commands to a single command ?

In fine, I'd like not having to do something like \bb{A}, \ms{A}, etc, that is, I don't just want an alias to these commands. What I am willing to achieve is to get rid of these curly brackets, for two reasons :

  1. As I've been using these commands for a few years now, muscle memory is going to be problematic.

  2. Typing Alt Gr+4, Alt Gr++, going Left, typing the letter and going Right again takes quite some time : this is the reason why I originally made these commands.

I'm not a LaTeX guru like some of you who already helped me a ton, and perhaps I just missed something when trying to find another approach than my "brute force" 130 lines... Thank you for your kind replies !

2
  • 8
    the braces are optional why not \newcommand\mf{\mathfrak} then use \mf Adefining all the extra commands seems totally unnecessary. Sep 25, 2020 at 20:20
  • @DavidCarlisle Thats wonderful! thanks.
    – IrbidMath
    Nov 3, 2020 at 18:11

2 Answers 2

12

Much less than 130 lines of code.

The idea is to do a loop in the given range; I use the alphabetic constants, so, for instance, A corresponds to 63, but it's not necessary to know it. The loop index is denoted by ##1 and will take in turn the ASCII number of the letters; with the help of \char_generate:nn we can generate the corresponding character. The definition is “expanded”, so we need not to expand the command, which is obtained by prefixing it with \exp_not:N.

\documentclass{article}
\usepackage{amsmath,amssymb}
\usepackage{xparse}

\ExplSyntaxOn

\NewDocumentCommand{\definealphabet}{mmmm}
 {% #1 = prefix, #2 = command, #3 = start, #4 = end
  \int_step_inline:nnn { `#3 } { `#4 }
   {
    \cs_new_protected:cpx { #1 \char_generate:nn { ##1 }{ 11 } }
     {
      \exp_not:N #2 { \char_generate:nn { ##1 } { 11 } }
     }
   }
 }

\ExplSyntaxOff

\definealphabet{bb}{\mathbb}{A}{Z}
\definealphabet{mc}{\mathcal}{A}{Z}
\definealphabet{mf}{\mathfrak}{A}{Z}
\definealphabet{mf}{\mathfrak}{a}{z}

\begin{document}

$\bbA+\mcB+\mfS+\mfz$

\end{document}

enter image description here

A more general method:

\documentclass{article}
\usepackage{amsmath,amsfonts}

\ExplSyntaxOn

\NewDocumentCommand{\definealphabet}{smmm}
 {% #1 = star for lowercase , #2 = prefix, #3 = suffix, #4 = math alphabet
  \IfBooleanTF { #1 }
   {
    \physicsstudent_definealphabet:nnnnn { #2 } { #3 } { `a } { `z } { #4 }
   }
   {
    \physicsstudent_definealphabet:nnnnn { #2 } { #3 } { `A } { `Z } { #4 }
   }
 }

\msg_new:nnn { physicsstudent/definealphabet } { existing }
 {
  Command ~ '\exp_not:c { #1 #2 #3 }' ~ already ~ defined
 }

\cs_new_protected:Nn \physicsstudent_definealphabet:nnnnn
 {
  \int_step_inline:nnn { #3 } { #4 }
   {
    \__physicsstudent_definealphabet:ennn { \char_generate:nn { ##1 } { 12 } } { #1 } { #2 } { #5 }
   }
 }

\cs_new_protected:Nn \__physicsstudent_definealphabet:nnnn
 {
  \cs_if_exist:cTF { #2 #1 #3 }
   {
    \msg_warning:nnnnn { physicsstudent/definealphabet } { existing } { #2 } { #1 } { #3 }
   }
   {
    \cs_new_protected:cpn { #2 #1 #3 } { #4 { #1 } }
   }
 }
\cs_generate_variant:Nn \__physicsstudent_definealphabet:nnnn { e }

\ExplSyntaxOff

\let\Bbb\relax % after seeing the warning
\definealphabet{}{bb}{\mathbb}
\definealphabet{frak}{}{\mathfrak}
\definealphabet*{frak}{}{\mathfrak}

\begin{document}

$\Abb+\frakA-\frakb$

\end{document}

You can specify either a prefix or a suffix (or even both). With the *-version, the lowercase alphabet is defined.

If you leave out the \let\Bbb\relax line, you'll get a warning

Package physicsstudent/definealphabet Warning: Command '\Bbb ' already defined

Examine the log file for such cases and decide whether the command can be safely redefined (with \Bbb it's possible, because it's only defined for compatibility with very old documents) or if a change in prefix/suffix is better.

enter image description here

1
  • Thanks this is very helpful.
    – IrbidMath
    Nov 3, 2020 at 18:09
5

The command \DEFINEbbANDmcANDmsANDmfLOOP is intended to process an undelimited argument which consists of exactly one token. It checks whether its argument's first token's meaning equals the meaning of the \relax-primitive.
If so, it swallows the following action via \@gobble.
If not so it assumes that the argument is a single character token denoting a ⟨letter⟩ and performs the following action via \@firstofone:

  • define the macro \bb⟨letter⟩ to expand to \mathbb{⟨letter⟩}
  • define the macro \mc⟨letter⟩ to expand to \mathcal{⟨letter⟩}
  • define the macro \ms⟨letter⟩ to expand to \mathscr{⟨letter⟩}
  • define the macro \mf⟨letter⟩ to expand to \mathfrakr{⟨letter⟩}
  • call itself again.

The concept of a macro calling itself again for applying the same action to another argument is called tail-recursion.

(For defining the macros I have implemented a little helper-macro \CmdName which works as follows:

\CmdName⟨stuff without braces⟩{macroname}⟨stuff without braces⟩\macroname.

Examples:

\CmdName\newcommand*{foobar}...\newcommand*\foobar....

I use \CmdName when I am not in the mood for typing \expandafter and \csname..\endcsname so often. ;-)  )

\documentclass{article}

\makeatletter
\@ifdefinable\CmdName{\long\def\CmdName#1#{\romannumeral0\innerCmdName{#1}}}%
\newcommand\innerCmdName[2]{\expandafter\exchange\expandafter{\csname #2\endcsname}{ #1}}%
\newcommand\exchange[2]{#2#1}%
\newcommand\DEFINEbbANDmcANDmsANDmfLOOP[1]{%
  \ifx\relax#1\expandafter\@gobble\else\expandafter\@firstofone\fi
  {%
    \CmdName\newcommand*{bb#1}{\mathbb{#1}}%
    \CmdName\newcommand*{mc#1}{\mathcal{#1}}%
    \CmdName\newcommand*{ms#1}{\mathscr{#1}}%
    \CmdName\newcommand*{mf#1}{\mathfrak{#1}}%
    \DEFINEbbANDmcANDmsANDmfLOOP
  }%
}%
\makeatother

% Now for each of the letters define \bb<letter>, \mc<letter>, \ms<letter> and \mf<letter>:

\DEFINEbbANDmcANDmsANDmfLOOP abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\relax

\begin{document}

\show\bba
\show\mca
\show\msa
\show\mfa

\show\bbb
\show\mcb
\show\msb
\show\mfb

%  . 
%  .
%  .
%  .

\show\bbY
\show\mcY
\show\msY
\show\mfY

\show\bbZ
\show\mcZ
\show\msZ
\show\mfZ

\end{document}

If you like, you can apply tail-recursion with a macro \Definealphabet:

\documentclass{article}

\makeatletter
\newcommand\DEFINEALPHABETLOOP[3]{%
  \ifx\relax#3\expandafter\@gobble\else\expandafter\@firstofone\fi
  {\expandafter\newcommand\expandafter*\csname#1#3\endcsname{#2{#3}}%
   \DEFINEALPHABETLOOP{#1}{#2}}%
}%
\newcommand\Definealphabet[2]{%
  \DEFINEALPHABETLOOP{#1}{#2}abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\relax
}%
\makeatother


\Definealphabet{bb}{\mathbb}
\Definealphabet{mc}{\mathcal}
\Definealphabet{ms}{\mathscr}
\Definealphabet{mf}{\mathfrak}


\begin{document}

\show\bba
% ...
\show\bbz


\show\mca
% ...
\show\mcz


\show\msa
% ...
\show\msz


\show\mfa
% ...
\show\mfz


\end{document}

If you need a TeX-⟨number⟩-quantity which denotes the character-code of a charcter in the internal character-representation-scheme of TeX, you can use alphabetic constants.
E.g., both `A and `\A denotes the character code of the character A.
The further is not "\lowercase-proof".
The latter in the context of macro-programming might not be proof against the control sequence token behind ` being \outer.
Also be aware that TeX scans for one optional space with the last token of an alphabetic constant.

Thus, if you like, you can as well implement a tail-recursive loop, where \csname..\endcsname is combined with \lccode/\lowercase-trickery for looping over the alphabet, and where you can specify slash-separated phrases for the math-alphabet-command to call and for the names of the commands to define the prefix before the alphabetical letter and the postfix behind the alphabetical letter:

\documentclass{article}

\makeatletter
\newcommand\PassFirstToSecond[2]{#2{#1}}%
\@ifdefinable\SlashFirstOfThree{\long\def\SlashFirstOfThree#1/#2/#3/{#1}}%
\@ifdefinable\SlashArgBetweenSecondAndThirdOfThree{\long\def\SlashArgBetweenSecondAndThirdOfThree#1#2/#3/#4/{#3#1#4}}%
\newcommand\DefineAlphabetsLoop[1]{\DefineAlphabetsLoopInner{`\A}#1\relax}%
\newcommand\DefineAlphabetsLoopInner[2]{%
  % #1 - character-code of character wherefore macro is to be defined; #2 - slash-separated triple of phrases:
  % of pattern  
  % <math-alphabet-macro to call>/<prefix before alphabet-letter of name of macro to define>/<prefix after alphabet-letter of name of macro to define>
  \ifx\relax#2\expandafter\@gobble\else\expandafter\@firstofone\fi{%
    \ifnum#1>\@firstofone{`\z} \expandafter\@secondoftwo\else\expandafter\@firstoftwo\fi
    %\@ifundefined{\SlashArgBetweenSecondAndThirdOfThree{z}#2/}%
    {%
      \begingroup
      \lccode`\m=#1\relax
      \lowercase\expandafter{\expandafter\PassFirstToSecond\expandafter{\SlashFirstOfThree#2/{m}}}%
      {%
         \lowercase{\PassFirstToSecond{m}}%
         {\endgroup\expandafter\newcommand\expandafter*\csname\SlashArgBetweenSecondAndThirdOfThree}#2/\endcsname
      }%
      \expandafter\DefineAlphabetsLoopInner\expandafter{\the\numexpr\ifnum#1=`\Z`\a\else(#1)+1\fi\relax}{#2}%
    }{%
      \DefineAlphabetsLoopInner{`\A}%
    }%
  }%
}%
\makeatother


\begin{document}

%-------------------------------------------------------------------------------------
% Only prefix:

\DefineAlphabetsLoop{{\mathbb/bb/}{\mathcal/mc/}{\mathscr/ms/}{\mathfrak/mf/}}%

\show\bbA \show\mcA \show\msA \show\mfA
\show\bbB \show\mcB \show\msB \show\mfB
\show\bbY \show\mcY \show\msY \show\mfY
\show\bbZ \show\mcZ \show\msZ \show\mfZ
%  . 
%  .
%  .
%  .
\show\bba \show\mca \show\msa \show\mfa
\show\bbb \show\mcb \show\msb \show\mfb
\show\bby \show\mcy \show\msy \show\mfy
\show\bbz \show\mcz \show\msz \show\mfz

%-------------------------------------------------------------------------------------
% Only postfix:

% Of course you get: ! LaTeX Error: Command \bbb already defined.
% That's because \bbb was defined by the prevous loop.

\DefineAlphabetsLoop{{\mathbb//bb}{\mathcal//mc}{\mathscr//ms}{\mathfrak//mf}}%

\show\Abb \show\Amc \show\Ams \show\Amf
\show\Bbb \show\Bmc \show\Bms \show\Bmf
\show\Ybb \show\Ymc \show\Yms \show\Ymf
\show\Zbb \show\Zmc \show\Zms \show\Zmf
%  . 
%  .
%  .
%  .
\show\abb \show\amc \show\ams \show\amf
\show\bbb \show\bmc \show\bms \show\bmf
\show\ybb \show\ymc \show\yms \show\ymf
\show\zbb \show\zmc \show\zms \show\zmf

%-------------------------------------------------------------------------------------
% Both prefix and postfix:

\DefineAlphabetsLoop{{\mathbb/bb/bb}{\mathcal/mc/mc}{\mathscr/ms/ms}{\mathfrak/mf/mf}}%

\show\bbAbb \show\mcAmc \show\msAms \show\mfAmf
\show\bbBbb \show\mcBmc \show\msBms \show\mfBmf
\show\bbYbb \show\mcYmc \show\msYms \show\mfYmf
\show\bbZbb \show\mcZmc \show\msZms \show\mfZmf
%  . 
%  .
%  .
%  .
\show\bbabb \show\mcamc \show\msams \show\mfamf
\show\bbbbb \show\mcbmc \show\msbms \show\mfbmf
\show\bbybb \show\mcymc \show\msyms \show\mfymf
\show\bbzbb \show\mczmc \show\mszms \show\mfzmf

%-------------------------------------------------------------------------------------
% Of course, instead of having three `\DefineAlphabetsLoop`, you could
% as well have done:
%
% \DefineAlphabetsLoop{%
%                       {\mathbb/bb/}{\mathcal/mc/}{\mathscr/ms/}{\mathfrak/mf/}%
%                       {\mathbb//bb}{\mathcal//mc}{\mathscr//ms}{\mathfrak//mf}%
%                       {\mathbb/bb/bb}{\mathcal/mc/mc}{\mathscr/ms/ms}{\mathfrak/mf/mf}%
%                     }%


\end{document}

A slightly more generic variant could be:

\alphabetloop{<stuff>} by and by does: <stuff>{A}...<stuff>{Z}<stuff>{a}...<stuff>{z}

and

\LoopEachOverAlphabet{<stuff prefix>}{<stuff postfix>}{%
    {<stuff 1>}%
    {<stuff 2>}%
    %...
    {<stuff n>}%
}%

by and by does

\alphabetloop{<stuff prefix><stuff 1><stuff postfix>}%
\alphabetloop{<stuff prefix><stuff 2><stuff postfix>}%
...
\alphabetloop{<stuff prefix><stuff n><stuff postfix>}%

, which by and by turns into

<stuff prefix><stuff 1><stuff postfix>{A}...<stuff prefix><stuff 1><stuff postfix>{Z}<stuff prefix><stuff 1><stuff postfix>{a}...<stuff prefix><stuff 1><stuff postfix>{z}%
<stuff prefix><stuff 2><stuff postfix>{A}...<stuff prefix><stuff 2><stuff postfix>{Z}<stuff prefix><stuff 2><stuff postfix>{a}...<stuff prefix><stuff 2><stuff postfix>{z}%
%...
<stuff prefix><stuff n><stuff postfix>{A}...<stuff prefix><stuff n><stuff postfix>{Z}<stuff prefix><stuff n><stuff postfix>{a}...<stuff prefix><stuff n><stuff postfix>{z}%

.

\documentclass{article}

\makeatletter
\newcommand\PassFirstToSecond[2]{#2{#1}}%
\newcommand\alphabetloop{\alphabetloopinner{`\A}}%
\newcommand\alphabetloopinner[2]{%
  \ifnum#1>\@firstofone{`\z} \expandafter\@gobble\else\expandafter\@firstofone\fi
  {%
    \begingroup
    \lccode`\m=#1\relax
    \lowercase{\PassFirstToSecond{m}}{\endgroup#2}%
    \expandafter\alphabetloopinner\expandafter{\the\numexpr\ifnum#1=`\Z`\a\else(#1)+1\fi\relax}{#2}%
  }%
}%
\newcommand\LoopEachOverAlphabet[3]{\LoopEachOverAlphabetInner{#1}{#2}#3\relax}%
\newcommand\LoopEachOverAlphabetInner[3]{%
  \ifx\relax#3\expandafter\@gobble\else\expandafter\@firstofone\fi{%
     \alphabetloop{#1#3#2}%
     \LoopEachOverAlphabetInner{#1}{#2}%
  }%
}%
\@ifdefinable\definealphabetmacro{%
  \long\def\definealphabetmacro#1/#2/#3/#4{\expandafter\newcommand\expandafter*\csname #2#4#3\endcsname{#1{#4}}}%
}%
\newcommand\DefineAlphabetMacros{%
  \LoopEachOverAlphabet{\definealphabetmacro}{/}%
}%
\makeatother

\newcommand\showalphabetmacro[3]{%
  \expandafter\show\csname #1#3#2\endcsname
}%


\begin{document}

%-------------------------------------------------------------------------------------
% Only prefix:

\DefineAlphabetMacros{{\mathbb/bb/}{\mathcal/mc/}{\mathscr/ms/}{\mathfrak/mf/}}%

%-------------------------------------------------------------------------------------
% Only postfix:

% Of course you get: ! LaTeX Error: Command \bbb already defined.
% That's because \bbb was defined by the prevous loop.

\DefineAlphabetMacros{{\mathbb//bb}{\mathcal//mc}{\mathscr//ms}{\mathfrak//mf}}%

%-------------------------------------------------------------------------------------
% Both prefix and postfix:

\DefineAlphabetMacros{{\mathbb/bb/bb}{\mathcal/mc/mc}{\mathscr/ms/ms}{\mathfrak/mf/mf}}%

%-------------------------------------------------------------------------------------
% Of course, instead of having three `\DefineAlphabetsLoop`, you could
% as well have done:
%
% \DefineAlphabetMacros{%
%                        {\mathbb/bb/}{\mathcal/mc/}{\mathscr/ms/}{\mathfrak/mf/}%
%                        {\mathbb//bb}{\mathcal//mc}{\mathscr//ms}{\mathfrak//mf}%
%                        {\mathbb/bb/bb}{\mathcal/mc/mc}{\mathscr/ms/ms}{\mathfrak/mf/mf}%
%                      }%

% This does \show  12*52  = 624 times. \bbb is shown twice:

\def\scratch{0}
\LoopEachOverAlphabet{%
  \edef\scratch{\the\numexpr\scratch+1\relax}%
  \message{^^JIteration \scratch:^^J}%
  \showalphabetmacro
}{}{%
  {{bb}{}} {{mc}{}}  {{ms}{}} {{mf}{}}
  {{}{bb}} {{}{mc}}  {{}{ms}} {{}{mf}}
  {{bb}{bb}} {{mc}{mc}}  {{ms}{ms}} {{mf}{mf}}
}%

\end{document}
2
  • I'm sorry I couldn't accept both answers ! Thanks a lot for sharing !
    – Anthony
    Sep 26, 2020 at 14:09
  • @AnthonySaint-Criq Instead of one of the answers I would accept the comment of David Carlisle if that was possible. :-) Sep 26, 2020 at 18:20

You must log in to answer this question.

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