12

I have a document that makes heavy use of predefined symbols (in order to later change them), i.e.:

\newcommand{\term}{t}
...

Now I need to introduce different annotated variants of the symbols:

\newcommand{\aterm}{\term^{\alpha}}
\newcommand{\bterm}{\term^{\beta}}
...

It is quite tedious to manually manage these sets (especially when adding a new symbol or removing an obsolete one).

So I naturally wonder, how one would define multiple (differently prefixed) arguments in one batch? Due to the macro capabilities of TeX, this is should certainly be possible, but is there a safe and convenient way?

i.e. is there some "meta"-newcommand that works somewhat like this:

\metanewcommand{\declaresymbols}[2]{\newcommand{\#1term}{t^{#2}} ... }

edit: Thanks to Steven's comment below, I was able to implement what I need myself:

\newcommand{\declaresymbol}[4]{
  %introduce a symbol \#3 -> #4 prefixed with #1 and annotated with #2
  \ifthenelse{\equal{#1}{}}{
    \expandafter\newcommand\csname#3\endcsname{#4}
  } {
    \expandafter\newcommand\csname#1#3\endcsname{#4‌​^{#2}}
  }
}

\newcommand{\declaresymbols}[2]{
  \declaresymbol{#1}{#2}{val}{v}
  \declaresymbol{#1}{#2}{term}{t}
}

% Standard symbol, no variant
\declaresymbols{}{} %gives \term -> t and \val -> v
% Alpha variant
\declaresymbols{a}{\alpha} %gives \aterm -> t^\alpha \aval -> v^\alpha
  • 1
    \newcommand{\declaresymbols}[2]{\expandafter\newcommand\csname#1term\endcsname{t^{#2}}}. Of course, \declaresymbols{a}{\alpha} is as hard to type as \newcommand{\aterm}{\term^{\alpha}} – Steven B. Segletes Oct 18 '16 at 9:27
  • Could you make this an answer? The \csname stuff was precisely the pointer, I needed. – choeger Oct 18 '16 at 10:07
9

What you are looking for is the plain-TeX directive \csname...\endcsname (cs for "control sequence"). Whereas the \<macro-name> syntax requires that the macro name be a predefined sequence of catcode 11 letters, what goes between the \csname...\endcsname delimiters can include symbols of other catcodes, and can include variables evaluated at the time of invocation.

The only trick, beyond this, is to use \expandafter prior to the defining \newcommand, so that the \csname gets expanded into the actual control sequence.

\documentclass{article}
\newcommand{\declaresymbols}[2]{\expandafter\newcommand\csname#1term\endcsname{t^{#2}}}
\begin{document}
\declaresymbols{a}{\alpha}
\declaresymbols{b}{\beta}

$\aterm \ne \bterm$
\end{document}

enter image description here

If you do a lot of work in the cs domain of programming, packages like etoolbox are extremely useful, providing whole families of macros for working with this tricky syntax in more natural ways.

  • I should add that another important distinction is that \<macro-name> produces an error if undefined, whereas \csname <macr-name>\endcsname produces \relax if undefined. The latter behavior can be used to test for definition. – Steven B. Segletes Oct 25 '16 at 1:31
6

You can do with

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand\definesymbol{omm}
 {
  \IfNoValueTF{#1}
   { \cs_new:cpn {#2} { #3 } }
   { \cs_new:cpn { #2 #1 } { \use:c { #1 } \sp { #3 } } }
 }
\ExplSyntaxOff

\definesymbol{term}{t}
\definesymbol[term]{a}{\alpha}
\definesymbol[term]{b}{\beta}

\definesymbol{X}{X}
\definesymbol[X]{a}{a}

\begin{document}

$\term+\aterm+\bterm+\X+\aX$

\end{document}

The call without the optional argument defines the main macro, with the optional argument it adds the superscripts and the prefix.

enter image description here


Since Ulrich Diez launched a contest, here it is a solution similar to his, but with much less code and a friendlier syntax.

\documentclass{article}

\usepackage{xparse}

\ExplSyntaxOn

\cs_new_protected:Nn \choeger_declare_single_symbol:nnnnnn
 {
  % {<macro-name of base-symbol>}
  % {<coding of base symbol>}
  % {<symbol-macro-name-prefix>}
  % {<symbol-macro-name-postfix>}
  % {<coding of preceding operands and operators>}
  % {<coding of trailing operands and operators>}
  \cs_new_protected:cpn { #3#1#4 } { #5{#2}#6 }
 }

\cs_new_protected:Nn \choeger_declare_symbol_family:nnn
 {
  % {<macro-name of base-symbol>}
  % {<coding of base symbol>}
  % {<list of sequences {prefix}{postfix}{prefix code}{postfix code}>}

  % define the base symbol
  \cs_new_protected:cpn { #1 } { {#2} }
  % define the others
  \clist_map_inline:nn { #3 }
   {
    \choeger_declare_single_symbol:nnnnnn { #1 } { #2 } ##1
   }
 }

\cs_new_protected:Nn \choeger_declare_symbol_families:nn
 {
  % {<list of sequences {name}{code}>}
  % {<list of sequences {prefix}{postfix}{prefix code}{postfix code}>}
  \clist_map_inline:nn { #1 }
   {
    \choeger_declare_symbol_family:nnn ##1 { #2 }
   }
 }

\NewDocumentCommand{\DeclareSymbolFamilies}{mm}
 {
  \choeger_declare_symbol_families:nn { #1 } { #2 }
 }

\ExplSyntaxOff

\DeclareSymbolFamilies
 {
  {Foo}{\langle\mathit{Foo}\rangle},
  {Term}{t},
  {Val}{v},
 }
 {
  {}{RaisedToPowerAlpha}{}{^\alpha},
  {MultiplyBetaWith}{}{\beta\cdot}{},
  {}{MultipiedWithGamma}{}{\cdot\gamma},
  {RaiseDeltaToThePowerOf}{}{\delta^}{},
  {MultiplyBetaWith}{AndAddTwo}{\beta\cdot}{+2},
  {MultiplyBetaWith}{AfterAddingTwo}{\beta\cdot(}{+2)},
  {DivideBetaBy}{}{\left(\frac{\beta}}{\right)},
  {Divide}{ByBeta}{\left(\frac}{{\beta}\right)},  
 }

\begin{document}
\setlength{\parindent}{0pt}

\verb|\Foo|: $\Foo$\\
\verb|\FooRaisedToPowerAlpha|: $\FooRaisedToPowerAlpha$\\
\verb|\MultiplyBetaWithFoo|: $\MultiplyBetaWithFoo$\\
\verb|\FooMultipiedWithGamma|: $\FooMultipiedWithGamma$\\
\verb|\RaiseDeltaToThePowerOfFoo|: $\RaiseDeltaToThePowerOfFoo$\\
\verb|\MultiplyBetaWithFooAndAddTwo|: $\MultiplyBetaWithFooAndAddTwo$\\
\verb|\MultiplyBetaWithFooAfterAddingTwo|: $\MultiplyBetaWithFooAfterAddingTwo$\\
\verb|\DivideBetaByFoo|: $\DivideBetaByFoo$\\
\verb|\DivideFooByBeta|: $\DivideFooByBeta$

\bigskip

\verb|\Term|: $\Term$\\
\verb|\TermRaisedToPowerAlpha|: $\TermRaisedToPowerAlpha$\\
\verb|\MultiplyBetaWithTerm|: $\MultiplyBetaWithTerm$\\
\verb|\TermMultipiedWithGamma|: $\TermMultipiedWithGamma$\\
\verb|\RaiseDeltaToThePowerOfTerm|: $\RaiseDeltaToThePowerOfTerm$\\
\verb|\MultiplyBetaWithTermAndAddTwo|: $\MultiplyBetaWithTermAndAddTwo$\\
\verb|\MultiplyBetaWithTermAfterAddingTwo|: $\MultiplyBetaWithTermAfterAddingTwo$\\
\verb|\DivideBetaByTerm|: $\DivideBetaByTerm$\\
\verb|\DivideTermByBeta|: $\DivideTermByBeta$

\bigskip

\verb|\Val|: $\Val$\\
\verb|\ValRaisedToPowerAlpha|: $\ValRaisedToPowerAlpha$\\
\verb|\MultiplyBetaWithVal|: $\MultiplyBetaWithVal$\\
\verb|\ValMultipiedWithGamma|: $\ValMultipiedWithGamma$\\
\verb|\RaiseDeltaToThePowerOfVal|: $\RaiseDeltaToThePowerOfVal$\\
\verb|\MultiplyBetaWithValAndAddTwo|: $\MultiplyBetaWithValAndAddTwo$\\
\verb|\MultiplyBetaWithValAfterAddingTwo|: $\MultiplyBetaWithValAfterAddingTwo$\\
\verb|\DivideBetaByVal|: $\DivideBetaByVal$\\
\verb|\DivideValByBeta|: $\DivideValByBeta$

\end{document}
2

Some nested loops might do the trick:

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

\makeatletter
%%-----------------------------------------------------------------------------
%% Check whether argument is empty:
%%.............................................................................
%% \UD@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>}%
%%
%% The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
%% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
%%
%% A concern in his posting is that the argument is hit with \string
%% after some expansions which in edge cases might result in unbalancing
%% surrounding \if..\fi-constructs if the macro is used inside of such
%% \if..\fi-constructs.
%%
%% That challenging concern sickened me. ;-)
%%
%% Therefore I decided to implerment a variant where this cannot happen
%% as expansion is forced by \romannumeral:
%%
%% After the first expansion-step, \string is not applied yet.
%% After the second expansion-step, any possibly disturbing remainders
%% are already removed due to \romannumeral-expansion.
%%
%% No eTeX- or whatsoever extensions. No \if.. .Only \romannumeral,
%% digit 0, space token for terminating \romannumeral-expansion,
%% \string, \expandafter, \@firstoftwo, \@secondoftwo, {, }.
\newcommand\UD@CheckWhetherNull[1]{%
  \romannumeral0\expandafter\@secondoftwo\string{\expandafter
  \@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
  \@secondoftwo\string}\expandafter\@firstoftwo\expandafter{\expandafter
  \@secondoftwo\string}\expandafter\expandafter\@firstoftwo{ }{}%
  \@secondoftwo}{\expandafter\expandafter\@firstoftwo{ }{}\@firstoftwo}%
}%
%%-----------------------------------------------------------------------------
%% Fully expandable for-loop:
%%.............................................................................
%% \UD@DoWithEachElementOfArgumentList{<action>}%
%%                             {<action when no (more) arguments are in list>}%
%%                             {<preset>}%
%%                             {{<e_k>}{<e_(k+1)>}..{<e_n>}}
%%
%%  yields (after two expansion-steps) :
%%
%%  <action>{<e_k>}<preset>%
%%  <action>{<e_(k+1)>}<preset>%
%%  ...
%%  <action>{<e_n>}<preset>%
%%  <action when no (more) arguments are in list>%
%%
\newcommand\UD@Exchange[2]{#2#1}%
\newcommand\UD@KeepOnlyFirstBeforeSeLDoM{}%
\long\def\UD@KeepOnlyFirstBeforeSeLDoM#1#2\SeLDoM{{#1}}%
\newcommand\UD@DoWithEachElementOfArgumentList{%
  \romannumeral0\UD@MoveElementFromList{}{ }%
}%
\newcommand\UD@MoveElementFromList[6]{%
  \UD@CheckWhetherNull{#1}{%
    \expandafter\UD@CheckWhetherNull\expandafter{\@secondoftwo#6.{}}{#2#4}{%
      \expandafter\expandafter\expandafter\UD@MoveElementFromList
      \expandafter\UD@Exchange
      \expandafter{%
      \expandafter{\@firstoftwo{}#6}}{{#6\SeLDoM}{#2}{#3}{#4}{#5}}%
    }%
  }{%
    \expandafter\UD@CheckWhetherNull\expandafter{\@firstoftwo{}#1}%
    {\UD@MoveElementFromList{}{#2#3#1#5}}%
    {\expandafter\UD@MoveElementFromList
     \expandafter{\UD@KeepOnlyFirstBeforeSeLDoM#1}{#2}%
    }{#3}{#4}{#5}{#6}%
  }%
}%

%%-----------------------------------------------------------------------------
%% \DeclareASingleSymbol{<symbol-macro-name-prefix>}%
%%                      {<symbol-macro-name-postfix>}%
%%                      {<coding of base symbol / coding of preceding operands and operators>}%
%%                      {<coding of trailing operands and operators>}%
%%                      {<macro-name of base-symbol>}
%%.............................................................................
\newcommand\DeclareASingleSymbol[5]{%
  \UD@CheckWhetherNull{#1#2}{%
    \expandafter\newcommand\csname#5\endcsname{#3}%
  }{%
    \expandafter\newcommand\csname#1#5#2%
    \expandafter\expandafter\expandafter\expandafter
    \expandafter\expandafter\expandafter\endcsname
    \expandafter\expandafter\expandafter\expandafter
    \expandafter\expandafter\expandafter{%
    \expandafter\expandafter\expandafter\UD@Exchange
    \expandafter\expandafter\expandafter{%
    \csname#5\endcsname#4}{#3}}%
  }%
}%
%%-----------------------------------------------------------------------------
%% \DeclareOneSymbolFamily
%%   {<macro-name of base-symbol>}%
%%   {<coding of base-symbol>}%
%%   {%
%%     {{<symbol-macro-name-prefix 1>}{<symbol-macro-name-postfix 1>}{<coding of preceding operands and operators 1>}{<coding of trailing operands and operators 1>}}%
%%     {{<symbol-macro-name-prefix 2>}{<symbol-macro-name-postfix 2>}{<coding of preceding operands and operators 2>}{<coding of trailing operands and operators 2>}}%
%%    ..
%%     {{<symbol-macro-name-prefix n>}{<symbol-macro-name-postfix n>}{<coding of preceding operands and operators n>}{<coding of trailing operands and operators n>}}%
%%   }%
%%
%%.............................................................................
\newcommand\DeclareOneSymbolFamily[3]{%
   \UD@DoWithEachElementOfArgumentList{\expandafter\DeclareASingleSymbol\@firstofone}%
                                      {}%
                                      {{#1}}%
                                      {{{}{}{#2}{}}#3}%
}%
%%-----------------------------------------------------------------------------
%% \DeclareSeveralSymbolFamilies{%
%%   {{<macro-name of base-symbol 1>}{<coding of base-symbol 1>}%
%%   {{<macro-name of base-symbol 2>}{<coding of base-symbol 2>}}%
%5   ..
%%   {{<macro-name of base-symbol k>}{<coding of base-symbol k>}}%
%% }{%
%%     {{<symbol-macro-name-prefix 1>}{<symbol-macro-name-postfix 1>}{<coding of preceding operands and operators 1>}{<coding of trailing operands and operators 1>}}%
%%     {{<symbol-macro-name-prefix 2>}{<symbol-macro-name-postfix 2>}{<coding of preceding operands and operators 2>}{<coding of trailing operands and operators 2>}}%
%%    ..
%%     {{<symbol-macro-name-prefix n>}{<symbol-macro-name-postfix n>}{<coding of preceding operands and operators n>}{<coding of trailing operands and operators n>}}%
%% }
%%
%%.............................................................................
\newcommand\DeclareSeveralSymbolFamilies[2]{%
   \UD@DoWithEachElementOfArgumentList{\expandafter\DeclareOneSymbolFamily\@firstofone}%
                                       {}%
                                       {{#2}}%
                                       {#1}%
}%

\makeatother

\DeclareSeveralSymbolFamilies{%
  {{Foo}{{\mathit{\text{\textlangle}Foo\text{\textrangle}}}}}%
  {{Term}{t}}%
  {{Val}{v}}%
}{%
  {{}{RaisedToPowerAlpha}{}{^\alpha}}%
  {{MulitplyBetaWith}{}{\beta\cdot}{}}%
  {{}{MulipliedWithGamma}{}{\cdot\gamma}}%
  {{RaiseDeltaToThePowerOf}{}{\delta^}{}}%
  {{MulitplyBetaWith}{AndAddTwo}{\beta\cdot}{+2}}%
  {{MulitplyBetaWith}{AfterAddingTwo}{\beta\cdot(}{+2)}}%
  {{DivideBetaBy}{}{\left(\frac{\beta}}{\right)}}%
  {{Divide}{ByBeta}{\left(\frac}{{\beta}\right)}}%  
}%

\begin{document}


\verb|\Foo|: $\Foo$\\
\verb|\FooRaisedToPowerAlpha|: $\FooRaisedToPowerAlpha$\\
\verb|\MulitplyBetaWithFoo|: $\MulitplyBetaWithFoo$\\
\verb|\FooMulipliedWithGamma|: $\FooMulipliedWithGamma$\\
\verb|\RaiseDeltaToThePowerOfFoo|: $\RaiseDeltaToThePowerOfFoo$\\
\verb|\MulitplyBetaWithFooAndAddTwo|: $\MulitplyBetaWithFooAndAddTwo$\\
\verb|\MulitplyBetaWithFooAfterAddingTwo|: $\MulitplyBetaWithFooAfterAddingTwo$\\
\verb|\DivideBetaByFoo|: $\DivideBetaByFoo$\\
\verb|\DivideFooByBeta|: $\DivideFooByBeta$\\


\verb|\Term|: $\Term$\\
\verb|\TermRaisedToPowerAlpha|: $\TermRaisedToPowerAlpha$\\
\verb|\MulitplyBetaWithTerm|: $\MulitplyBetaWithTerm$\\
\verb|\TermMulipliedWithGamma|: $\TermMulipliedWithGamma$\\
\verb|\RaiseDeltaToThePowerOfTerm|: $\RaiseDeltaToThePowerOfTerm$\\
\verb|\MulitplyBetaWithTermAndAddTwo|: $\MulitplyBetaWithTermAndAddTwo$\\
\verb|\MulitplyBetaWithTermAfterAddingTwo|: $\MulitplyBetaWithTermAfterAddingTwo$\\
\verb|\DivideBetaByTerm|: $\DivideBetaByTerm$\\
\verb|\DivideTermByBeta|: $\DivideTermByBeta$\\


\verb|\Val|: $\Val$\\
\verb|\ValRaisedToPowerAlpha|: $\ValRaisedToPowerAlpha$\\
\verb|\MulitplyBetaWithVal|: $\MulitplyBetaWithVal$\\
\verb|\ValMulipliedWithGamma|: $\ValMulipliedWithGamma$\\
\verb|\RaiseDeltaToThePowerOfVal|: $\RaiseDeltaToThePowerOfVal$\\
\verb|\MulitplyBetaWithValAndAddTwo|: $\MulitplyBetaWithValAndAddTwo$\\
\verb|\MulitplyBetaWithValAfterAddingTwo|: $\MulitplyBetaWithValAfterAddingTwo$\\
\verb|\DivideBetaByVal|: $\DivideBetaByVal$\\
\verb|\DivideValByBeta|: $\DivideValByBeta$\\

\end{document}                 

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.