2

I'm currently trying to define commands such that:

\x{F}{a} gives F^{(x=a)}
\y{F}{b} gives F^{(y=b)}

This I can do fairly easily:

\newcommand{\x}[2]{\ensuremath{{#1}^{(x={#2})}}}

However, I would like to have

\x{\y{F}{b}}{a} gives F^{(y=b, x=a)}

And this, I am currently not able to do.

In practice, I have many such commands \x, \y, \z, ... and I would like to be able to automatically combine them intelligently such that there is only one superscript (see example).

Does anyone know of a way to do this in TeX?

  • 1
    That syntax seems strange. Are you sure you want that (it is possible and I can post something, but it seems strange). – Skillmon Dec 4 '18 at 13:54
  • The idea is that I have several parametrized possible transformations on F, and I would like to say that \x{F}{a} is F transformed by transformation x with argument a. The problem is that I can also apply a transformation on something which has been transformed by another. The key thing, is that I do not want to have more than one superscript, which makes it non readable, and I can have the order of the transformations just by reading from left to right. – Serker Dec 4 '18 at 14:07
3

Have fun with it. Nested use is only correctly handled in the first argument of \x, \y etc. Create other macros with \newtransformation\<name>{symbol}.

\documentclass[]{article}

\usepackage[]{amsmath}

\newcommand\newtransformation[2]%>>>
  {%
    \newcommand*#1{\Fof{#2}}%
  }%<<<
\makeatletter
\def\q@stop{\q@stop}
\long\def\afterfi#1\fi{\fi#1}
\newcommand\Fof@test[3]%>>>
  {%
    \gdef\Fof@second{#2}%
    \xdef\Fof@pairs{\unexpanded\expandafter{\Fof@pairs{#1}{#3}}}%
    #2%
  }%<<<
\newcommand\Fof@printpair[3]%>>>
  {%
    #1=#2%
    \ifx\q@stop#3%
    \else
      \afterfi, \Fof@printpair{#3}%
    \fi
  }%<<<
\newcommand*\Fof@printpairs%>>>
  {%
    \expandafter\Fof@printpair\Fof@pairs\q@stop
  }%<<<
\newcommand*\Fof@output%>>>
  {%
    \Fof@second^{(\Fof@printpairs)}%
  }%<<<
\newcommand\Fof[3]%>>>
  {%
    \gdef\Fof@pairs{{#1}{#3}}%
    \gdef\Fof@second{#2}%
    \setbox\z@\hbox
      {% code analog to mathaccentV from amsmath
        \let\Fof\Fof@test
        \let\use@mathgroup\@gobbletwo
        \let\select@group\@gobblethree
        \frozen@everymath{}%
        $#2$%
      }%
    \Fof@output
  }%<<<
\makeatother

\newtransformation\x{x}
\newtransformation\y{y}

\begin{document}
$\x{\y{F}{c}}{b}$
\end{document}

enter image description here

To invert the order, you'd have to make sure that the new pairs are added to the front of \Fof@pairs. This can be done by changing the definition of \Fof@test. Change its definition to the following:

\newcommand\Fof@test[3]%>>>
  {%
    \gdef\Fof@second{#2}%
    \xdef\Fof@pairs{\unexpanded{{#1}{#3}}\unexpanded\expandafter{\Fof@pairs}}%
    #2%
  }%<<<
  • Impressive code, I think it is intended for only two variables? $\q{\x{\y{F}{c}}{b}}{a}$ doesn't seem to work (outputs ! Double superscript). – Marijn Dec 4 '18 at 14:25
  • @Marijn I never tested it with more than two because I thought it should work based on the code, but now it doesn't! I'll look into it. – Skillmon Dec 4 '18 at 14:29
  • @Marijn found the issue, forgot a #2. Should now work fine. – Skillmon Dec 4 '18 at 14:31
  • it does work now :) – Marijn Dec 4 '18 at 14:33
  • This is really impressive, I'll try and figure the code out but it works really well, thank you ! – Serker Dec 4 '18 at 14:43
4

Here's a way you could define these transformations.

I defined a macro \addtransform that normally just prints {#1}^{#2}, but if it used inside the first argument of another \addtransform it'll instead prepend #2, to the list of things to be placed in the superscript of the outermost \addtransform (and then print #1). (I hope that makes sense…)

The comments in the code below might be clearer.

\documentclass{article}

\let\currtransform\empty
\newcommand*\addtransform[2]{%
  \ifx\currtransform\empty                %% <- if we are not inside an \addtransform…
    \gdef\currtransform{#2}%              %% <- start a list of superscripts
    {#1}^{(\currtransform)}%              %% <- output (N.B. #1 may change \currtransform!)
    \global\let\currtransform\empty       %% <- clear the list
  \else                                   %% <- otherwise…
    \xdef\currtransform{\unexpanded{#2},% %% <- prepend #2 and a comma
      \unexpanded\expandafter{\currtransform}}%
    #1%                                   %% <- then print #1
  \fi
}

\newcommand*\transx[2]{\addtransform{#1}{x=#2}}
\newcommand*\transy[2]{\addtransform{#1}{y=#2}}
\newcommand*\transz[2]{\addtransform{#1}{z=#2}}

\begin{document}

\[
    \transx{F}{a},
\qquad
    \transx{\transy{F}{b}}{a},
\qquad
    \transx{\transy{\transz{F}{c}}{b}}{a}
\]

\end{document}

output

  • Very elegant and simple, +1. Mine is overly complicated because it actually does everything your \else branch does inside of a box. Yours should also be more robust if the first argument of any nested one contains more than just another nested one or something without one of those macros (mine would discard stuff in that case). – Skillmon Dec 4 '18 at 18:57
1

The syntax you propose is very complicated to maintain, perhaps a simpler one such as

\transform{x=a,y=b,z=c}{F}

is better. So I propose an implementation along this line (the argument is then printed from right to left), but also a rewriting of Circumscribe's very fine answer in expl3.

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn

% simpler
\NewDocumentCommand{\transform}{mm}
 {
  \seq_set_from_clist:Nn \l_tmpa_seq { #1 }
  \seq_reverse:N \l_tmpa_seq
  #2^{ ( \seq_use:Nn \l_tmpa_seq {,} ) }
 }

% recursive
\NewDocumentCommand{\definetransform}{m}
 {
  \clist_map_inline:nn { #1 }
   {
    \cs_new_protected:cpn { ##1 } ####1 ####2
     {
      \serker_transform:nnn { ##1 } { ####1 } { ####2 }
     }
   }
 }

\seq_new:N \g__serker_transform_seq

\cs_new_protected:Nn \serker_transform:nnn
 {
  \seq_if_empty:NTF \g__serker_transform_seq
   {
    \seq_gput_left:Nn \g__serker_transform_seq { #1 = #3 }
    {#2}^{ ( \seq_use:Nn \g__serker_transform_seq {,} ) }
    \seq_gclear:N \g__serker_transform_seq
   }
   {
    \seq_gput_left:Nn \g__serker_transform_seq { #1 = #3 }
    #2
   }
 }

\ExplSyntaxOff

\definetransform{x,y,z} % add to the list

\begin{document}

\[
\transform{x=a}{F},
\qquad
\transform{x=a,y=b}{F},
\qquad
\transform{x=a,y=b,z=c}{F}
\]

\[
\x{F}{a},
\qquad
\x{\y{F}{b}}{a},
\qquad
\x{\y{\z{F}{c}}{b}}{a}
\]

\end{document}

enter image description here

1
\documentclass{article}
\makeatletter
%%----------------------------------------------------------------------
%% Paraphernalia
%%----------------------------------------------------------------------
\newcommand\UD@firstoftwo[2]{#1}%
\newcommand\UD@secondoftwo[2]{#2}%
\newcommand\UD@Exchange[2]{#2#1}%
%%----------------------------------------------------------------------
%% 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>
%%
%% (\romannumeral expansion was introduced in order to overcome the
%%  concerns and worries about improperly balanced
%%  \if..\else..\fi constructs.)
%%
\newcommand\UD@CheckWhetherNull[1]{%
  \romannumeral0\expandafter\UD@secondoftwo\string{\expandafter
  \UD@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
  \UD@secondoftwo\string}\expandafter\UD@firstoftwo\expandafter{\expandafter
  \UD@secondoftwo\string}\expandafter\expandafter\UD@firstoftwo{ }{}%
  \UD@secondoftwo}{\expandafter\expandafter\UD@firstoftwo{ }{}\UD@firstoftwo}%
}%
%%----------------------------------------------------------------------
%% \UD@CheckWhetherLeadingUD@CollectMore{<Argument which is to be checked>}%
%%                             {<Tokens to be delivered in case <argument
%%                               which is to be checked>'s 1st token is 
%%                               \UD@CollectMore>}%
%%                             {<Tokens to be delivered in case <argument
%%                               which is to be checked>'s 1st token is not
%%                               \UD@CollectMore>}%
%%----------------------------------------------------------------------
\newcommand\UD@CheckWhetherLeadingUD@CollectMore[1]{%
  \romannumeral0\UD@CheckWhetherNull{#1}%
  {\expandafter\expandafter\UD@firstoftwo{ }{}\UD@secondoftwo}%
  {\expandafter\UD@secondoftwo\string{\UD@CheckWhetherLeadingUD@CollectMoreB.#1\UD@CollectMore}{}}%
}%
\newcommand\UD@CheckWhetherLeadingUD@CollectMoreB{}%
\long\def\UD@CheckWhetherLeadingUD@CollectMoreB#1\UD@CollectMore{%
  \expandafter\UD@CheckWhetherNull\expandafter{\UD@secondoftwo#1{}}%
  {\UD@Exchange{\UD@firstoftwo}}{\UD@Exchange{\UD@secondoftwo}}%
  {\UD@Exchange{ }{\expandafter\expandafter\expandafter\expandafter
   \expandafter\expandafter\expandafter}\expandafter\expandafter
   \expandafter}\expandafter\UD@secondoftwo\expandafter{\string}%
}%
%%......................................................................
\newcommand\UD@CollectMore{\UD@CollectMore@B{}}%
\newcommand\UD@CollectMore@B[3]{%
  \expandafter\UD@CheckWhetherLeadingUD@CollectMore\expandafter{#2}{%
    \expandafter\expandafter\expandafter\UD@Exchange
    \expandafter\expandafter\expandafter{%
    \expandafter\UD@firstoftwo\expandafter{\expandafter}#2}{\UD@CollectMore@B{, #3#1}}%
  }{%
    \ensuremath{{#2}^{(#3#1)}}%
  }%
}%
%%......................................................................

\newcommand\x[2]{\UD@CollectMore{#1}{x=#2}}%
\newcommand\y[2]{\UD@CollectMore{#1}{y=#2}}%
\newcommand\z[2]{\UD@CollectMore{#1}{z=#2}}%

\newcommand\U[2]{\UD@CollectMore{#1}{u=#2}}%
\newcommand\V[2]{\UD@CollectMore{#1}{v=#2}}%
\newcommand\W[2]{\UD@CollectMore{#1}{w=#2}}%

\makeatother

\begin{document}

\verb|\x{F}{a}|: % yields F^{(x=a)}
\x{F}{a}% gives F^{(x=a)}

\verb|\y{F}{b}|: % yields F^{(y=b)}
\y{F}{b}% gives F^{(y=b)}

\verb|\z{F}{c}|: % yields F^{(z=b)}
\z{F}{c}% gives F^{(z=b)}

\verb|\x{\y{F}{b}}{a}|: % yields F^{(y=b, x=a)}
\x{\y{F}{b}}{a}% gives F^{(y=b, x=a)}

\verb|\x{\y{\z{F}{c}}{b}}{a}|: % yields F^{(z=c, y=b, x=a)}
\x{\y{\z{F}{c}}{b}}{a}% gives F^{(z=c, y=b, x=a)}

\verb|\U{\V{\W{\x{\y{\z{F}{f}}{e}}{d}}{c}}{b}}{a}|: % yields F^{(z=f, y=e, x=d, w=c, v=b, u=a)}
\U{\V{\W{\x{\y{\z{F}{f}}{e}}{d}}{c}}{b}}{a}

\end{document}

enter image description here


With the example above, the routine for checking for another nested transformation does attempt top-level-expansion to the first token of a transformation-macro's first argument in any case.
In edge cases this might cause problems.

The example below includes a checking routine with some brace-hacks which does the check without attempting to top-level-expand the first token of a transformation-macro's first argument and which applies top-level-expansion to that token only in case it is one of the tokens that are used as transformation-macros.

Advantage:

This is more safe even in edge cases.

Disadvantages:

In case you wish to rename some of the transformation-macros or wish to add some transformation-macros, you need to adjust the checking-mechanism (\UD@MoreTransformationsFork, \UD@@MoreTransformationsFork) as well as it does check on the presence of tokens and not on their meaning.

The load of code is a bit larger: When grabbing the first token of an argument for checking whether it is either \x or \y or \z or the like, you also need to take into account that the very first token of the argument could as well be a space-token or a curly opening-brace or that the argument might be empty, and as you check on the presence of one of several tokens, you need code for cranking out several cases.

\documentclass{article}
\makeatletter
%%----------------------------------------------------------------------
%% Paraphernalia
%%----------------------------------------------------------------------
\newcommand\UD@firstoftwo[2]{#1}%
\newcommand\UD@secondoftwo[2]{#2}%
\newcommand\UD@Exchange[2]{#2#1}%
%%----------------------------------------------------------------------
%% 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>
\newcommand\UD@CheckWhetherNull[1]{%
  \romannumeral0\expandafter\UD@secondoftwo\string{\expandafter
  \UD@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
  \UD@secondoftwo\string}\expandafter\UD@firstoftwo\expandafter{\expandafter
  \UD@secondoftwo\string}\expandafter\expandafter\UD@firstoftwo{ }{}%
  \UD@secondoftwo}{\expandafter\expandafter\UD@firstoftwo{ }{}\UD@firstoftwo}%
}%
%%-----------------------------------------------------------------------------
%% Check whether argument's first token is a catcode-1-character
%%.............................................................................
%% \UD@CheckWhetherBrace{<Argument which is to be checked>}%
%%                      {<Tokens to be delivered in case that argument
%%                        which is to be checked has leading
%%                        catcode-1-token>}%
%%                      {<Tokens to be delivered in case that argument
%%                        which is to be checked has no leading
%%                        catcode-1-token>}%
\newcommand\UD@CheckWhetherBrace[1]{%
  \romannumeral0\expandafter\UD@secondoftwo\expandafter{\expandafter{%
  \string#1.}\expandafter\UD@firstoftwo\expandafter{\expandafter
  \UD@secondoftwo\string}\expandafter\expandafter\UD@firstoftwo{ }{}%
  \UD@firstoftwo}{\expandafter\expandafter\UD@firstoftwo{ }{}\UD@secondoftwo}%
}%
%%==============================================================================
%% Check whether brace-balanced argument starts with a space-token
%%..............................................................................
%% \UD@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\UD@CheckWhetherLeadingSpace[1]{%
  \romannumeral0\UD@CheckWhetherNull{#1}%
  {\expandafter\expandafter\UD@firstoftwo{ }{}\UD@secondoftwo}%
  {\expandafter\UD@secondoftwo\string{\UD@CheckWhetherLeadingSpaceB.#1 }{}}%
}%
\newcommand\UD@CheckWhetherLeadingSpaceB{}%
\long\def\UD@CheckWhetherLeadingSpaceB#1 {%
  \expandafter\UD@CheckWhetherNull\expandafter{\UD@secondoftwo#1{}}%
  {\UD@Exchange{\UD@firstoftwo}}{\UD@Exchange{\UD@secondoftwo}}%
  {\UD@Exchange{ }{\expandafter\expandafter\expandafter\expandafter
   \expandafter\expandafter\expandafter}\expandafter\expandafter
   \expandafter}\expandafter\UD@secondoftwo\expandafter{\string}%
}%
%%-----------------------------------------------------------------------------
%% Check whether argument does not contain "!":
%%.............................................................................
\newcommand\UD@GobbleToExclam{}%
\long\def\UD@GobbleToExclam#1!{}%
\newcommand\UD@CheckWhetherNoExclam[1]{%
  \expandafter\UD@CheckWhetherNull\expandafter{\UD@GobbleToExclam#1!}%
}%
%%----------------------------------------------------------------------
%% \UD@CheckWhetherLeadingUD@CollectMore{<Argument which is to be checked>}%
%%                             {<Tokens to be delivered in case <argument
%%                               which is to be checked>'s 1st token is 
%%                               \UD@CollectMore>}%
%%                             {<Tokens to be delivered in case <argument
%%                               which is to be checked>'s 1st token is not
%%                               \UD@CollectMore>}%
%%----------------------------------------------------------------------
\newcommand\UD@CheckForMoreTransformations[1]{%
   \romannumeral0\UD@CheckWhetherBrace{#1}{%
     \expandafter\expandafter\UD@firstoftwo{ }{}\UD@secondoftwo
   }{%
     \UD@CheckWhetherLeadingSpace{#1}{%
       \expandafter\expandafter\UD@firstoftwo{ }{}\UD@secondoftwo
     }{%
        \UD@CheckWhetherNull{#1}{%
           \expandafter\expandafter\UD@firstoftwo{ }{}\UD@secondoftwo
        }{%
          \expandafter\UD@secondoftwo\string{\UD@MoreTransformationsFork#1}{}%
        }%
     }%
   }%
}%
\newcommand\UD@MoreTransformationsFork[1]{%
  \UD@CheckWhetherNoExclam{#1}{%
    \UD@@MoreTransformationsFork!#1!\V!\W!\x!\y!\z!{\UD@Exchange{\UD@firstoftwo}}% \U
                                !\U!#1!\W!\x!\y!\z!{\UD@Exchange{\UD@firstoftwo}}% \V
                                !\U!\V!#1!\x!\y!\z!{\UD@Exchange{\UD@firstoftwo}}% \W
                                !\U!\V!\W!#1!\y!\z!{\UD@Exchange{\UD@firstoftwo}}% \x
                                !\U!\V!\W!\x!#1!\z!{\UD@Exchange{\UD@firstoftwo}}% \y
                                !\U!\V!\W!\x!\y!#1!{\UD@Exchange{\UD@firstoftwo}}% \z
                                !\U!\V!\W!\x!\y!\z!{\UD@Exchange{\UD@secondoftwo}}% something else without !
                                !!!!%
  }{\UD@Exchange{\UD@secondoftwo}}% something else with !
  {\UD@Exchange{ }{\expandafter\expandafter\expandafter\expandafter
   \expandafter\expandafter\expandafter}\expandafter\expandafter
   \expandafter}\expandafter\UD@secondoftwo\expandafter{\string}%
}%
\newcommand\UD@@MoreTransformationsFork{}%
\long\def\UD@@MoreTransformationsFork#1!\U!\V!\W!\x!\y!\z!#2#3!!!!{#2}%
%%......................................................................
\newcommand\UD@CollectMore{\UD@CollectMore@B{}}%
\newcommand\UD@CollectMore@B[3]{%
  \UD@CheckForMoreTransformations{#2}{%
    \expandafter\expandafter\expandafter\UD@Exchange
    \expandafter\expandafter\expandafter{%
    \expandafter\UD@firstoftwo\expandafter{\expandafter}#2}{\UD@CollectMore@B{, #3#1}}%
  }{%
    \ensuremath{{#2}^{(#3#1)}}%
  }%
}%
%%......................................................................

\newcommand\x[2]{\UD@CollectMore{#1}{x=#2}}%
\newcommand\y[2]{\UD@CollectMore{#1}{y=#2}}%
\newcommand\z[2]{\UD@CollectMore{#1}{z=#2}}%

\newcommand\U[2]{\UD@CollectMore{#1}{u=#2}}%
\newcommand\V[2]{\UD@CollectMore{#1}{v=#2}}%
\newcommand\W[2]{\UD@CollectMore{#1}{w=#2}}%

\makeatother

\begin{document}

\verb|\x{F}{a}|: % yields F^{(x=a)}
\x{F}{a}% gives F^{(x=a)}

\verb|\y{F}{b}|: % yields F^{(y=b)}
\y{F}{b}% gives F^{(y=b)}

\verb|\z{F}{c}|: % yields F^{(z=b)}
\z{F}{c}% gives F^{(z=b)}

\verb|\x{\y{F}{b}}{a}|: % yields F^{(y=b, x=a)}
\x{\y{F}{b}}{a}% gives F^{(y=b, x=a)}

\verb|\x{\y{\z{F}{c}}{b}}{a}|: % yields F^{(z=c, y=b, x=a)}
\x{\y{\z{F}{c}}{b}}{a}% gives F^{(z=c, y=b, x=a)}

\verb|\U{\V{\W{\x{\y{\z{F}{f}}{e}}{d}}{c}}{b}}{a}|: % yields F^{(z=f, y=e, x=d, w=c, v=b, u=a)}
\U{\V{\W{\x{\y{\z{F}{f}}{e}}{d}}{c}}{b}}{a}

\end{document}

enter image description here

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.