改变一个命令在另一个命令中的效果

改变一个命令在另一个命令中的效果

我目前正在尝试定义如下命令:

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

我可以相当轻松地做到这一点:

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

不过,我希望

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

而这一点,我目前还做不到。

在实践中,我有许多这样的命令\x,,,...,我希望能够自动智能地组合它们,使得只有一个上标(参见示例)。\y\z

有人知道在 TeX 中如何做到这一点吗?

答案1

玩得开心。嵌套使用仅在 等的第一个参数中正确处理\x\y使用 创建其他宏\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}

在此处输入图片描述

要反转顺序,您必须确保将新对添加到 的前面\Fof@pairs。这可以通过更改 的定义来实现\Fof@test。将其定义更改为以下内容:

\newcommand\Fof@test[3]%>>>
  {%
    \gdef\Fof@second{#2}%
    \xdef\Fof@pairs{\unexpanded{{#1}{#3}}\unexpanded\expandafter{\Fof@pairs}}%
    #2%
  }%<<<

答案2

您可以通过以下方式定义这些转换。

我定义了一个宏\addtransform,通常只打印{#1}^{#2},但如果它在另一个宏的第一个参数中使用,\addtransform它会将要#2,放置在最外层上标中的事物列表添加到前面\addtransform(然后打印#1)。(我希望这有意义……)

下面代码中的注释可能更清楚。

\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}

输出

答案3

您建议的语法维护起来非常复杂,也许可以使用更简单的语法,例如

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

更好。因此我建议按照这种方式实现(然后从右到左打印参数),但也重写 Circumscribe 中非常出色的答案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}

在此处输入图片描述

答案4

\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}

在此处输入图片描述


对于上述示例,检查另一个嵌套转换的例程无论如何都会尝试对转换宏的第一个参数的第一个标记进行顶层扩展。
在极端情况下,这可能会导致问题。

下面的示例包括一个带有一些大括号黑客的检查例程,该例程在进行检查时不会尝试对转换宏的第一个参数的第一个标记进行顶层扩展,并且仅当它是用作转换宏的标记之一时才对该标记应用顶层扩展。

优势:

即使在极端情况下这也更安全。

缺点:

如果你想重命名一些转换宏或者希望添加一些转换宏,你需要调整检查机制(\UD@MoreTransformationsFork\UD@@MoreTransformationsFork),因为它会检查标记的存在而不是它们的含义。

代码负载稍微大一些:当抓取参数的第一个标记来检查它是否是“或”\x\y“或\z”之类的时,您还需要考虑到参数的第一个标记也可能是空格标记或花括号,或者参数可能为空,并且当您检查多个标记之一的存在时,您需要代码来处理多个案例。

\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}

在此处输入图片描述

相关内容