我想定义两个宏 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}