如何在不完全展开输入的情况下编写一个if
子句来检查立即展开的第一个元素是否为?\mymacro
\documentclass{minimal}
\def\mymacro#1{Does something with #1}
\def\foo{\mymacro{1}}
\def\bar{\mymacro{2}}
\def\baz{\mymacro{3}}
\def\qux{\mymacro{4}\extra}
\def\mymacrosecond{\something\mymacro{3}}
\def\nomymacro{\something\different}
\def\hiddenmymacro{\foo}
\newcommand{\startswithmymacro}[1]{%
% tests if the immediate expansion of #1 starts with \mymacro
% 1. expand #1 once (something with \expandonce??)
% 2. store first token, discard the rest (something with \gobble??)
% 3. compare first token with \mymacro (\ifx\first\mymacro)
}
\begin{document}
\startswithmymacro{\foo} % true
\startswithmymacro{\bar} % true
\startswithmymacro{\baz} % true
\startswithmymacro{\qux} % true
\startswithmymacro{\mymacrosecond} % false
\startswithmymacro{\nomymacro} % false
\startswithmymacro{\hiddenmymacro} % false
\end{document}
答案1
您自己实际上已经很好地描述了这些步骤。唯一缺少的是实现它们的代码:-)
\documentclass{article}
\def\mymacro#1{Does something with #1}
\def\foo{\mymacro{1}}
\def\bar{\mymacro{2}}
\def\baz{\mymacro{3}}
\def\qux{\mymacro{4}\extra}
\def\mymacrosecond{\something\mymacro{3}}
\def\nomymacro{\something\different}
\def\hiddenmymacro{\foo}
\makeatletter
\newcommand{\startswithmymacro}[1]{%
\begingroup
\edef\@tempa{\unexpanded\expandafter{#1}}%
\def\@tempb##1##2\quark{\def\@tempc{##1}}%
\expandafter\@tempb\@tempa\quark
\expandafter\ifx\@tempc\mymacro T\else F\fi
\endgroup
}
\makeatother
\begin{document}
(\startswithmymacro{\foo},T) % true
(\startswithmymacro{\bar},T) % true
(\startswithmymacro{\baz},T) % true
(\startswithmymacro{\qux},T) % true
(\startswithmymacro{\mymacrosecond},F) % false
(\startswithmymacro{\nomymacro},F) % false
(\startswithmymacro{\hiddenmymacro},F) % false
\end{document}
答案2
\futurelet
参数通过链式扩展一次后,就可以使用TeX 原语了\expandafter
:
\def\startswithtokenmymacro#1{%
\expandafter\futurelet\expandafter\next\expandafter\swtmmA#1\swtmmA}
\def\swtmmA#1\swtmmA{\ifx\next\mymacro TRUE\else FALSE\fi}
%% Tests:
\def\foo{\mymacro{1}}
\def\bar{\mymacro{2}}
\def\baz{\mymacro{3}}
\def\qux{\mymacro{4}\extra}
\def\mymacrosecond{\something\mymacro{3}}
\def\nomymacro{\something\different}
\def\hiddenmymacro{\foo}
\def\mymacroinbraces{{\mymacro{1}}something}
\def\mymacrocopyfoo{\mymacrocopy{1}}
\def\mymacrocopybar{\mymacrocopy{2}}
\def\mymacrocopybaz{\mymacrocopy{3}}
\def\mymacrocopyqux{\mymacrocopy{4}\extra}
\def\mymacrosecond{\something\mymacrocopy{3}}
\def\mymacrocopyinbraces{{\mymacrocopy{1}}something}
\def\bat{{\mymacro}{1}}%
\def\bau{{xx\fi}}
\def\bav{}
\def\baw{ }
\startswithtokenmymacro{\foo} - expected: true
\startswithtokenmymacro{\bar} - expected: true
\startswithtokenmymacro{\baz} - expected: true
\startswithtokenmymacro{\qux} - expected: true
\startswithtokenmymacro{\romannumeral0 \mymacro} - expected: true
\startswithtokenmymacro{\csname mymacro\endcsname} - expected: true
\startswithtokenmymacro{\mymacrosecond} - expected: false
\startswithtokenmymacro{\nomymacro} - expected: false
\startswithtokenmymacro{\hiddenmymacro} - expected: false
\startswithtokenmymacro{\mymacroinbraces} - expected: false
\startswithtokenmymacro{\mymacrocopyfoo} - expected: false
\startswithtokenmymacro{\mymacrocopybar} - expected: false
\startswithtokenmymacro{\mymacrocopybaz} - expected: false
\startswithtokenmymacro{\mymacrocopyqux} - expected: false
\startswithtokenmymacro{\mymacrosecond} - expected: false
\startswithtokenmymacro{\mymacrocopyinbraces} - expected: false
\startswithtokenmymacro{{\foo}} - expected: false
\startswithtokenmymacro{\bat} - expected: false
\startswithtokenmymacro{\bau} - expected: false
\startswithtokenmymacro{\bav} - expected: false
\startswithtokenmymacro{\baw} - expected: false
\bye
答案3
(在实际生活中的大多数情况下,可扩展性和将标记标记放置在参数的最后一个标记后面不是问题。因此在实际生活中wipet 的回答绝对是首选。我的回答是,可扩展性是一个问题,因为需要(这似乎不太可能)或由于“学术兴趣”。)
如果你想测试“命中”\startswithtokenmymacro
参数的第一个标记的结果是否\expandafter
有第一个标记恰好是控制字标记\mymacro
,只要该结果中的任何标记都不是,下面的方法可能会起作用\outer
:
\makeatletter
%%=============================================================================
\newcommand\UD@firstoftwo[2]{#1}%
\newcommand\UD@secondoftwo[2]{#2}%
\newcommand\UD@Exchange[2]{#2#1}%
\@ifdefinable\UD@stopromannumeral{\chardef\UD@stopromannumeral=`\^^00}%
%%-----------------------------------------------------------------------------
%% 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]{%
\romannumeral\expandafter\UD@secondoftwo\string{\expandafter
\UD@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
\UD@secondoftwo\string}\expandafter\UD@firstoftwo\expandafter{\expandafter
\UD@secondoftwo\string}\expandafter\UD@stopromannumeral\UD@secondoftwo}{%
\expandafter\UD@stopromannumeral\UD@firstoftwo}%
}%
%%-----------------------------------------------------------------------------
%% Check whether brace-balanced argument has a first token which
%% is exactly the token \mymacro
%%.............................................................................
%% \UDCheckWhetherLeadingMymacro{<Argument which is to be checked>}%
%% {<Tokens to be delivered in case <argument
%% which is to be checked> does have a
%% leading \mymacro>}%
%% {<Tokens to be delivered in case <argument
%% which is to be checked> does not have a
%% a leading \mymacro>}%
\newcommand\UDCheckWhetherLeadingMymacro[1]{%
\romannumeral\UD@CheckWhetherNull{#1}%
{\expandafter\UD@stopromannumeral\UD@secondoftwo}%
{%
% Let's nest things into \UD@firstoftwo{...}{} to make sure they are nested in braces
% and thus do not disturb when the test is carried out within \halign/\valign:
\expandafter\UD@firstoftwo\expandafter{%
\expandafter\expandafter\expandafter\UD@stopromannumeral
\romannumeral\expandafter\UD@secondoftwo
\string{\UD@CheckWhetherLeadingMymacroB.#1\mymacro}{}%
}{}%
}%
}%
\@ifdefinable\UD@CheckWhetherLeadingMymacroB{%
\long\def\UD@CheckWhetherLeadingMymacroB#1\mymacro{%
\expandafter\UD@CheckWhetherNull\expandafter{\UD@firstoftwo{}#1}%
{\UD@Exchange{\UD@firstoftwo}}{\UD@Exchange{\UD@secondoftwo}}%
{\expandafter\expandafter\expandafter\UD@stopromannumeral
\expandafter\expandafter\expandafter}%
\expandafter\UD@secondoftwo\expandafter{\string}%
}%
}%
%%=============================================================================
\makeatother
\documentclass{minimal}
%% As the test is on the presence of tokens, not on meanings of possibly
%% defined macros, the following two definitions are not needed:
%\def\mymacro#1{Does something with #1}
%\let\mymacrocopy=\mymacro
\def\foo{\mymacro{1}}
\def\bar{\mymacro{2}}
\def\baz{\mymacro{3}}
\def\qux{\mymacro{4}\extra}
\def\mymacrosecond{\something\mymacro{3}}
\def\nomymacro{\something\different}
\def\hiddenmymacro{\foo}
\def\mymacroinbraces{{\mymacro{1}}something}
\def\mymacrocopyfoo{\mymacrocopy{1}}
\def\mymacrocopybar{\mymacrocopy{2}}
\def\mymacrocopybaz{\mymacrocopy{3}}
\def\mymacrocopyqux{\mymacrocopy{4}\extra}
\def\mymacrosecond{\something\mymacrocopy{3}}
\def\mymacrocopyinbraces{{\mymacrocopy{1}}something}
\def\bat{{\mymacro}{1}}%
\def\bau{{xx\fi}}
\def\bav{}
\def\baw{ }
\newcommand{\startswithtokenmymacro}[1]{%
\expandafter\UDCheckWhetherLeadingMymacro\expandafter{#1}{true}{false}%
}
\begin{document}
\startswithtokenmymacro{\foo} - expected: true
\startswithtokenmymacro{\bar} - expected: true
\startswithtokenmymacro{\baz} - expected: true
\startswithtokenmymacro{\qux} - expected: true
\startswithtokenmymacro{\romannumeral0 \mymacro} - expected: true
\startswithtokenmymacro{\csname mymacro\endcsname} - expected: true
\startswithtokenmymacro{\mymacrosecond} - expected: false
\startswithtokenmymacro{\nomymacro} - expected: false
\startswithtokenmymacro{\hiddenmymacro} - expected: false
\startswithtokenmymacro{\mymacroinbraces} - expected: false
\startswithtokenmymacro{\mymacrocopyfoo} - expected: false
\startswithtokenmymacro{\mymacrocopybar} - expected: false
\startswithtokenmymacro{\mymacrocopybaz} - expected: false
\startswithtokenmymacro{\mymacrocopyqux} - expected: false
\startswithtokenmymacro{\mymacrosecond} - expected: false
\startswithtokenmymacro{\mymacrocopyinbraces} - expected: false
\startswithtokenmymacro{{\foo}} - expected: false
\startswithtokenmymacro{\bat} - expected: false
\startswithtokenmymacro{\bau} - expected: false
\startswithtokenmymacro{\bav} - expected: false
\startswithtokenmymacro{\baw} - expected: false
\end{document}
如果你想测试“命中”\startswithmeaningofmymacro
参数的第一个标记的结果是否\expandafter
有与标记具有相同含义的第一个标记\mymacro
,只要该结果的标记都不是,下面的方法可能会起作用\outer
:
检查结果是否为空。如果是,则结果中的第一个标记就不具有与标记 具有相同含义的第一个标记\mymacro
。如果结果不为空,则检查结果的第一个标记是否为第 1 类(开始组)的显式字符标记,例如左花括号。如果是,则结果中的第一个标记就不具有与标记 具有相同含义的第一个标记。如果不是,则检查结果的第一个标记是否为显式空格标记。如果是,则结果中的第一个标记就不具有与标记 具有相同含义的第一个标记。如果不是,则可以通过未分隔的宏参数和括号技巧安全地提取该结果的第一个标记,然后可以通过将其含义与标记 的含义进行比较:{1\mymacro
\mymacro
\ifx
\mymacro
\makeatletter
%%=============================================================================
%% PARAPHERNALIA:
%% \UD@firstoftwo, \UD@secondoftwo, \UD@PassFirstToSecond, \UD@Exchange,
%% \UD@stopromannumeral, \UD@CheckWhetherNull,
%% \UD@CheckWhetherBrace, \UD@CheckWhetherLeadingExplicitSpace,
%% \UD@CheckWhetherMeaningsEqual, \UD@ExtractFirstArg
%%=============================================================================
\newcommand\UD@firstoftwo[2]{#1}%
\newcommand\UD@secondoftwo[2]{#2}%
\newcommand\UD@PassFirstToSecond[2]{#2{#1}}%
\newcommand\UD@Exchange[2]{#2#1}%
\@ifdefinable\UD@stopromannumeral{\chardef\UD@stopromannumeral=`\^^00}%
%%-----------------------------------------------------------------------------
%% 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]{%
\romannumeral\expandafter\UD@secondoftwo\string{\expandafter
\UD@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
\UD@secondoftwo\string}\expandafter\UD@firstoftwo\expandafter{\expandafter
\UD@secondoftwo\string}\expandafter\UD@stopromannumeral\UD@secondoftwo}{%
\expandafter\UD@stopromannumeral\UD@firstoftwo}%
}%
%%-----------------------------------------------------------------------------
%% Check whether argument's first token is an explicit character token
%% of category 1 (begin group):
%%.............................................................................
%% \UD@CheckWhetherBrace{<Argument which is to be checked>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked has a leading
%% explicit catcode-1-character-token>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked does not have a
%% leading explicit catcode-1-character-token>}%
\newcommand\UD@CheckWhetherBrace[1]{%
\romannumeral\expandafter\UD@secondoftwo\expandafter{\expandafter{%
\string#1.}\expandafter\UD@firstoftwo\expandafter{\expandafter
\UD@secondoftwo\string}\expandafter\UD@stopromannumeral\UD@firstoftwo}{%
\expandafter\UD@stopromannumeral\UD@secondoftwo}%
}%
%%-----------------------------------------------------------------------------
%% Check whether brace-balanced argument's first token is an explicit
%% space-token:
%%.............................................................................
%% \UD@CheckWhetherLeadingExplicitSpace{<Argument which is to be checked>}%
%% {<Tokens to be delivered in case <argument
%% which is to be checked> does have a
%% leading explicit space-token>}%
%% {<Tokens to be delivered in case <argument
%% which is to be checked> does not have a
%% a leading explicit space-token>}%
\newcommand\UD@CheckWhetherLeadingExplicitSpace[1]{%
\romannumeral\UD@CheckWhetherNull{#1}%
{\expandafter\UD@stopromannumeral\UD@secondoftwo}%
{%
% Let's nest things into \UD@firstoftwo{...}{} to make sure they are nested in braces
% and thus do not disturb when the test is carried out within \halign/\valign:
\expandafter\UD@firstoftwo\expandafter{%
\expandafter\expandafter\expandafter\UD@stopromannumeral
\romannumeral\expandafter\UD@secondoftwo
\string{\UD@CheckWhetherLeadingExplicitSpaceB.#1 }{}%
}{}%
}%
}%
\@ifdefinable\UD@CheckWhetherLeadingExplicitSpaceB{%
\long\def\UD@CheckWhetherLeadingExplicitSpaceB#1 {%
\expandafter\UD@CheckWhetherNull\expandafter{\UD@firstoftwo{}#1}%
{\UD@Exchange{\UD@firstoftwo}}{\UD@Exchange{\UD@secondoftwo}}%
{\expandafter\expandafter\expandafter\UD@stopromannumeral
\expandafter\expandafter\expandafter}%
\expandafter\UD@secondoftwo\expandafter{\string}%
}%
}%
%%-----------------------------------------------------------------------------
%% Check whether meanings of two tokens are equal.
%%.............................................................................
\newcommand\UD@CheckWhetherMeaningsEqual[2]{%
\romannumeral
\expandafter\UD@firstoftwo\expandafter{%
\romannumeral\ifx#1#2%
\expandafter\UD@stopromannumeral\expandafter\expandafter
\expandafter\UD@stopromannumeral\expandafter\UD@firstoftwo\else
\expandafter\UD@stopromannumeral\expandafter\expandafter
\expandafter\UD@stopromannumeral\expandafter\UD@secondoftwo\fi
}{}%
}%
%%-----------------------------------------------------------------------------
%% Extract first inner undelimited argument:
%%
%% \UD@ExtractFirstArg{ABCDE} yields {A}
%%
%% \UD@ExtractFirstArg{{AB}CDE} yields {AB}
%%
%% Due to \romannumeral-expansion the result is delivered after two
%% expansion-steps/after "hitting" \ExtractFirstArg with \expandafter
%% twice.
%%
%% \UD@ExtractFirstArg's argument must not be blank.
%%
%% Use frozen-\relax as delimiter for speeding things up.
%% I chose frozen-\relax because David Carlisle pointed out in
%% <https://tex.stackexchange.com/a/578877>
%% that frozen-\relax cannot be (re)defined in terms of \outer and cannot be
%% affected by \uppercase/\lowercase.
%%
%% \ExtractFirstArg's argument may contain frozen-\relax:
%% The only effect is that internally more iterations are needed for
%% obtaining the result.
%%
%%.............................................................................
\@ifdefinable\UD@RemoveTillFrozenrelax{%
\expandafter\expandafter\expandafter\UD@Exchange
\expandafter\expandafter\expandafter{%
\expandafter\expandafter\ifnum0=0\fi}%
{\long\def\UD@RemoveTillFrozenrelax#1#2}{{#1}}%
}%
\expandafter\UD@PassFirstToSecond\expandafter{%
\romannumeral\expandafter
\UD@PassFirstToSecond\expandafter{\romannumeral
\expandafter\expandafter\expandafter\UD@Exchange
\expandafter\expandafter\expandafter{%
\expandafter\expandafter\ifnum0=0\fi}{\UD@stopromannumeral#1{}}%
}{%
\UD@stopromannumeral\romannumeral\UD@ExtractFirstArgLoop
}%
}{%
\newcommand\UD@ExtractFirstArg[1]%
}%
\newcommand\UD@ExtractFirstArgLoop[1]{%
\expandafter\UD@CheckWhetherNull\expandafter{\UD@firstoftwo{}#1}%
{\UD@stopromannumeral#1}%
{\expandafter\UD@ExtractFirstArgLoop\expandafter{\UD@RemoveTillFrozenrelax#1}}%
}%
%%=============================================================================
%% Check whether brace-balanced argument has a first token whose meaning
%% equals the meaning of the token \mymacro
%%.............................................................................
%% \UDCheckWhetherLeadingMymacroMeaning
%% {<Argument which is to be checked>}%
%% {<Tokens to be delivered in case <argument which is to be checked> does
%% have a first token whose meaning equals the meaning of the token
%% \mymacro>}%
%% {<Tokens to be delivered in case <argument which is to be checked> does
%% not have a first token whose meaning equals the meaning of the token
%% \mymacro>}%
\newcommand\UDCheckWhetherLeadingMymacroMeaning[1]{%
\romannumeral\UD@CheckWhetherNull{#1}{\UD@secondoftwo}%
{%
\UD@CheckWhetherBrace{#1}{\UD@secondoftwo}%
{%
\UD@CheckWhetherLeadingExplicitSpace{#1}{\UD@secondoftwo}%
{%
\expandafter\expandafter\expandafter
\UD@CheckWhetherMeaningsEqual
\UD@ExtractFirstArg{#1}{\mymacro}{\UD@firstoftwo}{\UD@secondoftwo}%
}%
}%
}%
{\expandafter\UD@stopromannumeral\UD@firstoftwo}%
{\expandafter\UD@stopromannumeral\UD@secondoftwo}%
}%
%%=============================================================================
\makeatother
\documentclass{minimal}
%% As the test is on meanings of tokens, he following two definitions
%% affect the resukts of the test:
\def\mymacro#1{Does something with #1}
\let\mymacrocopy=\mymacro
\def\foo{\mymacro{1}}
\def\bar{\mymacro{2}}
\def\baz{\mymacro{3}}
\def\qux{\mymacro{4}\extra}
\def\mymacrosecond{\something\mymacro{3}}
\def\nomymacro{\something\different}
\def\hiddenmymacro{\foo}
\def\mymacroinbraces{{\mymacro{1}}something}
\def\mymacrocopyfoo{\mymacrocopy{1}}
\def\mymacrocopybar{\mymacrocopy{2}}
\def\mymacrocopybaz{\mymacrocopy{3}}
\def\mymacrocopyqux{\mymacrocopy{4}\extra}
\def\mymacrosecond{\something\mymacrocopy{3}}
\def\mymacrocopyinbraces{{\mymacrocopy{1}}something}
\def\bat{{\mymacro}{1}}
\def\bau{{xx\fi}}
\def\bav{}
\def\baw{ }
\newcommand{\startswithmeaningofmymacro}[1]{%
\expandafter\UDCheckWhetherLeadingMymacroMeaning\expandafter{#1}{true}{false}%
}
\begin{document}
\ttfamily
\startswithmeaningofmymacro{\foo} - expected: true
\startswithmeaningofmymacro{\bar} - expected: true
\startswithmeaningofmymacro{\baz} - expected: true
\startswithmeaningofmymacro{\qux} - expected: true
\startswithmeaningofmymacro{\mymacrocopyfoo} - expected: true
\startswithmeaningofmymacro{\mymacrocopybar} - expected: true
\startswithmeaningofmymacro{\mymacrocopybaz} - expected: true
\startswithmeaningofmymacro{\mymacrocopyqux} - expected: true
\startswithmeaningofmymacro{\romannumeral0 \mymacro} - expected: true
\startswithmeaningofmymacro{\romannumeral0 \mymacrocopy} - expected: true
\startswithmeaningofmymacro{\csname mymacro\endcsname} - expected: true
\startswithmeaningofmymacro{\csname mymacrocopy\endcsname} - expected: true
\startswithmeaningofmymacro{\mymacrosecond} - expected: false
\startswithmeaningofmymacro{\nomymacro} - expected: false
\startswithmeaningofmymacro{\hiddenmymacro} - expected: false
\startswithmeaningofmymacro{\mymacroinbraces} - expected: false
\startswithmeaningofmymacro{\mymacrocopyinbraces} - expected: false
\startswithmeaningofmymacro{{\foo}} - expected: false
\startswithmeaningofmymacro{\bat} - expected: false
\startswithmeaningofmymacro{\bau} - expected: false
\startswithmeaningofmymacro{\bav} - expected: false
\startswithmeaningofmymacro{\baw} - expected: false
\end{document}
查看代码时,您有时会看到一些内容被包裹在 extra\UD@firstoftwo{...}{}
或类似内容中。我这样做是为了确保用户在每个扩展步骤(最后一个提供结果的步骤除外)之后提供的作为宏参数的标记都嵌套在一对匹配的花括号之间。这反过来适用于在 LaTeX 扫描对齐元素时应用宏机制的情况,而这反过来又发生在执行tabular
环境等过程中。在这种情况下,如果内容没有嵌套在括号之间,则宏机制启动的扩展级联可能会被 TeX 对齐元素的扫描拦截。
顺便说一句:如果您很挑剔,那么\ifx
当您想知道两个令牌(例如宏令牌)是否触发相同的动作/扩展级联时,您可以说这并不完全可靠。
例如,在下面的例子中,宏\macroA
、\macroB
、\macroC
的\macroD
作用完全相同。尽管如此,在\ifx
-comparison 中,如果在其定义的 ⟨参数文本⟩ 中使用类别 6(参数)的不同字符标记来表示参数,则它们被视为不同:
\newlinechar=`\^^J %
% Let's use Z in the same way as #, i.e., for denoting parameter:
\catcode`\Z=6 %
% Let's define some macros which do exactly the same:
\def\macroA#1{In parentheses the result of processing the argument: (#1)}%
\def\macroB#1{In parentheses the result of processing the argument: (Z1)}%
\def\macroCZ1{In parentheses the result of processing the argument: (Z1)}%
\def\macroDZ1{In parentheses the result of processing the argument: (#1)}%
\def\space{ }%
% Reset Z to be a letter:
\catcode`\Z=11 %
\def\printmessagecompare#1#2{%
\message{%
^^J%
\string\ifx\string#1\string#2\space equal\string\else\space different\string\fi\space yields:
\ifx#1#2 equal\else different\fi
}%
}%
\message{^^JThis is the result of \string\macroA{Argument}: \macroA{Argument}}%
\message{^^JThis is the result of \string\macroB{Argument}: \macroB{Argument}}%
\message{^^JThis is the result of \string\macroC{Argument}: \macroC{Argument}}%
\message{^^JThis is the result of \string\macroC{Argument}: \macroC{Argument}}%
\printmessagecompare\macroA\macroA
\printmessagecompare\macroA\macroB
\printmessagecompare\macroA\macroC
\printmessagecompare\macroA\macroD
\printmessagecompare\macroB\macroA
\printmessagecompare\macroB\macroB
\printmessagecompare\macroB\macroC
\printmessagecompare\macroB\macroD
\printmessagecompare\macroC\macroA
\printmessagecompare\macroC\macroB
\printmessagecompare\macroC\macroC
\printmessagecompare\macroC\macroD
\printmessagecompare\macroD\macroA
\printmessagecompare\macroD\macroB
\printmessagecompare\macroD\macroC
\printmessagecompare\macroD\macroD
\csname stop\endcsname
\bye
您会在终端/控制台/shell 上收到以下消息:
This is the result of \macroA{Argument}: In parentheses the result of processin
g the argument: (Argument)
This is the result of \macroB{Argument}: In parentheses the result of processin
g the argument: (Argument)
This is the result of \macroC{Argument}: In parentheses the result of processin
g the argument: (Argument)
This is the result of \macroC{Argument}: In parentheses the result of processin
g the argument: (Argument)
\ifx\macroA\macroA equal\else different\fi yields: equal
\ifx\macroA\macroB equal\else different\fi yields: equal
\ifx\macroA\macroC equal\else different\fi yields: different
\ifx\macroA\macroD equal\else different\fi yields: different
\ifx\macroB\macroA equal\else different\fi yields: equal
\ifx\macroB\macroB equal\else different\fi yields: equal
\ifx\macroB\macroC equal\else different\fi yields: different
\ifx\macroB\macroD equal\else different\fi yields: different
\ifx\macroC\macroA equal\else different\fi yields: different
\ifx\macroC\macroB equal\else different\fi yields: different
\ifx\macroC\macroC equal\else different\fi yields: equal
\ifx\macroC\macroD equal\else different\fi yields: equal
\ifx\macroD\macroA equal\else different\fi yields: different
\ifx\macroD\macroB equal\else different\fi yields: different
\ifx\macroD\macroC equal\else different\fi yields: equal
\ifx\macroD\macroD equal\else different\fi yields: equal
例如,不处理任何参数且执行完全相同的操作但没有使用相同前缀组合(如 \long
或\outer
或) 定义的宏在-comparison\protected
中被视为不同:\ifx
\newlinechar=`\^^J %
% Let's define some macros which do exactly the same:
\def\macroA{Expansion of the macro.}%
\long\def\macroB{Expansion of the macro.}%
\def\space{ }%
\def\printmessagecompare#1#2{%
\message{%
^^J%
\string\ifx\string#1\string#2\space equal\string\else\space different\string\fi\space yields:
\ifx#1#2 equal\else different\fi
}%
}%
\message{^^JThis is the result of \string\macroA: \macroA}%
\message{^^JThis is the result of \string\macroB: \macroB}%
\printmessagecompare\macroA\macroA
\printmessagecompare\macroB\macroB
\printmessagecompare\macroA\macroB
\printmessagecompare\macroB\macroA
\csname stop\endcsname
\bye
您会在终端/控制台/shell 上收到以下消息:
This is the result of \macroA: Expansion of the macro.
This is the result of \macroB: Expansion of the macro.
\ifx\macroA\macroA equal\else different\fi yields: equal
\ifx\macroB\macroB equal\else different\fi yields: equal
\ifx\macroA\macroB equal\else different\fi yields: different
\ifx\macroB\macroA equal\else different\fi yields: different
答案4
这个答案的要点是wipet 的回答,其中\futurelet
使用。
然而,稍微修改一下就可以了:
如果不需要可扩展性并且不喜欢使用带分隔符的宏参数,则需要指定一个参数分隔符,该分隔符作为“标记标记”来查找使用“命中”参数的第一个标记的结果的最后一个标记,\expandafter
因此它不能出现在使用“命中”参数的第一个标记的结果中\expandafter
,您可以结合使用\futurelet
并\afterassignment
坚持使用无分隔符的宏参数:
\newcommand{\startswithmeaningofmymacro}[1]{%
\begingroup
\afterassignment\RemoveArgAndCompareArgsFirstTokensMeaning
\expandafter\futurelet\expandafter\Scratchtoken\expandafter{#1}%
}%
\newcommand\RemoveArgAndCompareArgsFirstTokensMeaning[1]{%
\expandafter\endgroup
\ifx\Scratchtoken\mymacro TRUE\else FALSE\fi
}%
\documentclass{minimal}
%% As the test is on meanings of tokens, the following two definitions
%% affect the results of the test:
\def\mymacro#1{Does something with #1}
\let\mymacrocopy=\mymacro
\def\foo{\mymacro{1}}
\def\bar{\mymacro{2}}
\def\baz{\mymacro{3}}
\def\qux{\mymacro{4}\extra}
\def\mymacrosecond{\something\mymacro{3}}
\def\nomymacro{\something\different}
\def\hiddenmymacro{\foo}
\def\mymacroinbraces{{\mymacro{1}}something}
\def\mymacrocopyfoo{\mymacrocopy{1}}
\def\mymacrocopybar{\mymacrocopy{2}}
\def\mymacrocopybaz{\mymacrocopy{3}}
\def\mymacrocopyqux{\mymacrocopy{4}\extra}
\def\mymacrosecond{\something\mymacrocopy{3}}
\def\mymacrocopyinbraces{{\mymacrocopy{1}}something}
\def\bat{{\mymacro}{1}}
\def\bau{{xx\fi}}
\def\bav{}
\def\baw{ }
\begin{document}
\ttfamily
\startswithmeaningofmymacro{\foo} - expected: TRUE
\startswithmeaningofmymacro{\bar} - expected: TRUE
\startswithmeaningofmymacro{\baz} - expected: TRUE
\startswithmeaningofmymacro{\qux} - expected: TRUE
\startswithmeaningofmymacro{\mymacrocopyfoo} - expected: TRUE
\startswithmeaningofmymacro{\mymacrocopybar} - expected: TRUE
\startswithmeaningofmymacro{\mymacrocopybaz} - expected: TRUE
\startswithmeaningofmymacro{\mymacrocopyqux} - expected: TRUE
\startswithmeaningofmymacro{\romannumeral0 \mymacro} - expected: TRUE
\startswithmeaningofmymacro{\romannumeral0 \mymacrocopy} - expected: TRUE
\startswithmeaningofmymacro{\csname mymacro\endcsname} - expected: TRUE
\startswithmeaningofmymacro{\csname mymacrocopy\endcsname} - expected: TRUE
\startswithmeaningofmymacro{\mymacrosecond} - expected: FALSE
\startswithmeaningofmymacro{\nomymacro} - expected: FALSE
\startswithmeaningofmymacro{\hiddenmymacro} - expected: FALSE
\startswithmeaningofmymacro{\mymacroinbraces} - expected: FALSE
\startswithmeaningofmymacro{\mymacrocopyinbraces} - expected: FALSE
\startswithmeaningofmymacro{{\foo}} - expected: FALSE
\startswithmeaningofmymacro{\bat} - expected: FALSE
\startswithmeaningofmymacro{\bau} - expected: FALSE
\startswithmeaningofmymacro{\bav} - expected: FALSE
\startswithmeaningofmymacro{\baw} - expected: FALSE
Of course this can be fooled in case the result of hitting the first
token of the argument of \verb|\startswithmeaningofmymacro| with
\verb|\expandafter| yields an empty argument while \verb|\mymacro|
is let equal to \verb|}|:
\let\mymacro=}
\startswithmeaningofmymacro{\romannumeral0 } - expected: FALSE
\startswithmeaningofmymacro{} - expected: FALSE
So you might wish to separately crank out the case of emptiness.
\end{document}
\startswithmeaningofmymacro
当然,如果命中with参数的第一个标记的结果
\expandafter
产生一个空参数而\mymacro
让 等于}
,那么这可能会被欺骗。
因此,您可能希望单独解决空虚的情况:
\newcommand{\startswithmeaningofmymacro}[1]{%
\begingroup
\afterassignment\RemoveArgAndCompareArgsFirstTokensMeaning
\expandafter\futurelet\expandafter\Scratchtoken\expandafter{#1}%
}%
\newcommand\RemoveArgAndCompareArgsFirstTokensMeaning[1]{%
\expandafter\expandafter\expandafter\endgroup
\expandafter\ifx\expandafter\Scratchtoken\expandafter
\mymacro\expandafter\ifcat\expandafter$%
\detokenize{#1}$FALSE\else TRUE\fi\else FALSE\fi
}%
% In case you work on an engine where \detokenize is not available,
% use expl3's \tl_if_empty:nTF{#1}{TRUE}{FALSE} or
% \UD@CheckWhetherNull{#1}{TRUE}{FALSE} from my other answer
% instead of \ifcat$\detokenize{#1}$FALSE\else TRUE\fi.
\documentclass{minimal}
%% As the test is on meanings of tokens, the following two definitions
%% affect the results of the test:
\def\mymacro#1{Does something with #1}
\let\mymacrocopy=\mymacro
\def\foo{\mymacro{1}}
\def\bar{\mymacro{2}}
\def\baz{\mymacro{3}}
\def\qux{\mymacro{4}\extra}
\def\mymacrosecond{\something\mymacro{3}}
\def\nomymacro{\something\different}
\def\hiddenmymacro{\foo}
\def\mymacroinbraces{{\mymacro{1}}something}
\def\mymacrocopyfoo{\mymacrocopy{1}}
\def\mymacrocopybar{\mymacrocopy{2}}
\def\mymacrocopybaz{\mymacrocopy{3}}
\def\mymacrocopyqux{\mymacrocopy{4}\extra}
\def\mymacrosecond{\something\mymacrocopy{3}}
\def\mymacrocopyinbraces{{\mymacrocopy{1}}something}
\def\bat{{\mymacro}{1}}
\def\bau{{xx\fi}}
\def\bav{}
\def\baw{ }
\begin{document}
\ttfamily
\startswithmeaningofmymacro{\foo} - expected: TRUE
\startswithmeaningofmymacro{\bar} - expected: TRUE
\startswithmeaningofmymacro{\baz} - expected: TRUE
\startswithmeaningofmymacro{\qux} - expected: TRUE
\startswithmeaningofmymacro{\mymacrocopyfoo} - expected: TRUE
\startswithmeaningofmymacro{\mymacrocopybar} - expected: TRUE
\startswithmeaningofmymacro{\mymacrocopybaz} - expected: TRUE
\startswithmeaningofmymacro{\mymacrocopyqux} - expected: TRUE
\startswithmeaningofmymacro{\romannumeral0 \mymacro} - expected: TRUE
\startswithmeaningofmymacro{\romannumeral0 \mymacrocopy} - expected: TRUE
\startswithmeaningofmymacro{\csname mymacro\endcsname} - expected: TRUE
\startswithmeaningofmymacro{\csname mymacrocopy\endcsname} - expected: TRUE
\startswithmeaningofmymacro{\mymacrosecond} - expected: FALSE
\startswithmeaningofmymacro{\nomymacro} - expected: FALSE
\startswithmeaningofmymacro{\hiddenmymacro} - expected: FALSE
\startswithmeaningofmymacro{\mymacroinbraces} - expected: FALSE
\startswithmeaningofmymacro{\mymacrocopyinbraces} - expected: FALSE
\startswithmeaningofmymacro{{\foo}} - expected: FALSE
\startswithmeaningofmymacro{\bat} - expected: FALSE
\startswithmeaningofmymacro{\bau} - expected: FALSE
\startswithmeaningofmymacro{\bav} - expected: FALSE
\startswithmeaningofmymacro{\baw} - expected: FALSE
%-----------------------------------------------------------------
\let\mymacro=}
\startswithmeaningofmymacro{\romannumeral0 } - expected: FALSE
\startswithmeaningofmymacro{} - expected: FALSE
\end{document}