指定两个宏的连接行为

指定两个宏的连接行为

我想定义两个宏 x 和 y,这样它们都有某种实现,但在我们在 y 上调用 x 的特定情况下,结果不同。例如,我必须如何更改以下定义

\newcommand{\x}[1]{Foo #1}
\newcommand{\y}[1]{Bar #1}

获取代码

\x{SomeExpression}
\y{SomeExpression}
\x{\y{SomeExpression}}
\x{\y{SomeExpression}\y{SomeExpression}}

产生以下输出:

Foo 某些表达式的结果

某些表达式的结果

你在 y 之后调用了 x

Foo Bar 某些表达式的结果Bar 某些表达式的结果

这里 SomeExpression 是一个表达式,其计算结果为字符串 ResultOfSomeExpression。我不在乎如果你做了这样的事情会发生什么,比如调用

\x{\y}

因为它不是在我的应用程序中预期使用的。

答案1

在此处输入图片描述

\documentclass{article}
\begin{document}
\makeatletter
\newcommand{\x}[1]{%
\expandafter\def\expandafter\zz\expandafter{\@firstoftwo#1..}%
\ifx\z\zz 
You called x after y%
\else
Foo #1%
\fi}
\newcommand{\y}[1]{Bar #1}
\def\z{\y..}



\x{a}

\y{a}

\x{\y{a}}

\x{\y{a}\y{a}}

\x{SomeExpression}

\y{SomeExpression}

\x{\y{SomeExpression}}

\x{\y{SomeExpression}\y{SomeExpression}}
\end{document}

答案2

在 TeX 和 LaTeX 中,一切都与处理标记、扩展(可扩展)宏标记等有关。

您不需要正确地(用文字)指定一个算法来描述宏的期望行为\x以及\y处理其参数/其参数的标记时的情况,而只需指定四种行为情况:

  • 案例 1:\x{SomeExpression}
                    →Foo ResultOfSomeExpression

  • 情况 2:\y{SomeExpression}
                    →Bar ResultOfSomeExpression

  • 案例 3:\x{\y{SomeExpression}}
                    →You called x after y

  • 案例 4:\x{\y{SomeExpression}\y{SomeExpression}}
                    →Foo Bar ResultOfSomeExpressionBar ResultOfSomeExpression

你确定在案例 4 中你确实希望得到这个结果
Foo Bar ResultOfSomeExpressionBar ResultOfSomeExpression
但你又不希望得到这个结果
Foo ResultOfBar ResultOfSomeExpressionBar ResultOfSomeExpression吗?

也许一些括号会更好地表明结合性?——类似于:
Foo ResultOf(Bar ResultOf(SomeExpression)Bar ResultOf(SomeExpression))

是因为它可能。

下面我实现了一个宏\y和一个宏\x

\y只是“抓取”它的参数(即删除分隔括号)并返回Bar ResultOf添加了前导短语的该参数:
Bar ResultOf<Argument>

该宏\x抓取其参数(即删除分隔括号)并分叉三种情况:

情况 1:参数的模式为
⟨control-sequence-token \y⟩ + ⟨single undelimited parameters⟩
在这种情况下,您会得到以下短语:You called x after y

情况 2:参数的模式为
⟨control-sequence-token \y⟩ + ⟨某些不是单个未限定参数的东西,也可能是空⟩
在这种情况下,您会得到短语Foo <Argument>

情况 3:参数为任何其他模式,也可能为空。
在这种情况下,您会得到短语Foo ResultOf<Argument>

请注意,我既没有使用任何 eTeX 扩展或类似的东西,也没有使用任何\if..\else..\fi-token,也没有进行临时分配。

不发生临时分配的情况使得宏在扩展上下文中也可以使用。

例如,如果您(例如就\@namedef)定义了一个名为的宏标记\You called x after y,则可以通过调用该宏\csname\x{\y{something}}\endcsname

\documentclass[a4paper]{article}
%%=============================================================================
%% Paraphernalia:
%%    \UD@firstoftwo, \UD@secondoftwo, \UD@Exchange,
%%    \UD@CheckWhetherNull, \UD@CheckWhetherSingleUndelimitedArg
%%=============================================================================
\makeatletter
\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>
%%
%% 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, \UD@firstoftwo, \UD@secondoftwo, {, }.
%%
%% May 20, 2016
%%
%% Ulrich Diez (e-mail: [email protected])
%%
\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 itself consists of exactly one undelimited 
%% argument:
%%.............................................................................
\newcommand\UD@CheckWhetherSingleDelimitedArg[1]{%
  \romannumeral0%
  \UD@CheckWhetherNull{#1}{\UD@secondoftwo{}}{%
    \expandafter\UD@CheckWhetherNull\expandafter{\UD@secondoftwo#1{}}%
    {\expandafter\expandafter\UD@firstoftwo{ }{}\UD@firstoftwo}%
  }%
  {\expandafter\expandafter\UD@firstoftwo{ }{}\UD@secondoftwo}%
}%
%%=============================================================================
%% Internal macros of a mechanism checking whether a macro argument is of
%% pattern \y<single undelimited argument> -- the user-level-macro will be
%% \UDAtCheckWhetherLeadingyAndSingleUndelimited 
%%-----------------------------------------------------------------------------
%% Check whether brace-balanced argument starts with a \y-token
%%.............................................................................
%% \UDAtCheckWhetherLeadingy{<Argument which is to be checked>}%
%%                         {<Tokens to be delivered in case <argument
%%                            which is to be checked>'s 1st token is a
%%                            \y-token>}%
%%                         {<Tokens to be delivered in case <argument
%%                           which is to be checked>'s 1st token is not
%%                           a \y-token>}%
\newcommand\UDAtCheckWhetherLeadingy[1]{%
  \romannumeral0\UD@CheckWhetherNull{#1}%
  {\expandafter\expandafter\UD@firstoftwo{ }{}\UD@secondoftwo}%
  {\expandafter\UD@secondoftwo\string{\UD@CheckWhetherLeadingyB.#1\y}{}}%
}%
\newcommand\UD@CheckWhetherLeadingyB{}%
\long\def\UD@CheckWhetherLeadingyB#1\y{%
  \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 brace-balanced argument is of pattern
%% \y<single undelimited argument>
%%.............................................................................
\newcommand\UDAtCheckWhetherLeadingyAndSingleUndelimited[1]{%
  \romannumeral0%
  \UDAtCheckWhetherLeadingy{#1}{%
    \expandafter\UD@CheckWhetherSingleDelimitedArg
    \expandafter{\UD@firstoftwo{}#1}{%
      \expandafter\expandafter\UD@firstoftwo{ }{}\UD@firstoftwo        
    }%
  }{\UD@secondoftwo{}}%
  {%
    \expandafter\expandafter\UD@firstoftwo{ }{}\UD@secondoftwo
  }%
}%
\makeatother

%% This delivers the texts that you specified.

\newcommand\x[1]{%
  \UDAtCheckWhetherLeadingyAndSingleUndelimited{#1}{%
    You called x after y%
  }{%
   \UDAtCheckWhetherLeadingy{#1}{Foo #1}{Foo ResultOf#1}%
  }%
}%
\newcommand\y[1]{Bar ResultOf#1}%    

%%\newcommand\x[1]{%
%%  \UDAtCheckWhetherLeadingyAndSingleUndelimited{#1}{%
%%    operation x with the result of applying operation y to #1 as operand%
%%  }{%
%%   operation x with operand (#1)%
%%  }%
%%}%
%%
%%newcommand\y[1]{operation y with operand (#1)}%


\begin{document}

\x{SomeExpression}

\y{SomeExpression}

\x{\y{SomeExpression}}

\x{\y{SomeExpression}\y{SomeExpression}}

\makeatletter
\@namedef{You called x after y}{Calling x after y is nice!}
\@namedef{Foo ResultOfSomethingWeird}{Calling x on something weird is nice too!}
\@namedef{Bar ResultOfSomethingWeird}{Calling y on something weird is nice as well!}    
\@namedef{Foo Bar ResultOfSomethingWeirdBar ResultOfSomethingWeird}{Calling x on the square of result of calling y on something weird is extraordinarily nice!}        
\makeatother

\csname\x{\y{something weird}}\endcsname

\csname\x{SomethingWeird}\endcsname

\csname\y{SomethingWeird}\endcsname    

\csname\x{\y{SomethingWeird}\y{SomethingWeird}}\endcsname        

\end{document}

相关内容