宏本身知道它们的宏名吗?

宏本身知道它们的宏名吗?

#0一些编程或脚本语言知道或的功能$0,保存当前脚本或当前例程的名称。

TeX/LaTeX 中的宏名是否存在类似的技术(或解决方法)?

为了澄清我的问题:

是否可以在辅助宏中提取宏本身中的宏名称,例如\CurrentMacroName宏/命令\FooBar包含一个内部宏\def\CurrentMacroName{FooBar},该内部宏只有在\FooBar扩展时才有效。

我之所以询问是因为我想在我的定义中提供一些\GenericWarning/\GenericError消息\newcommand,引用当前的宏名称而不必再次输入...我想以通用的方式执行它 - 当然,单独的消息必须根据每个命令进行调整。

一个小的不起作用的例子...(如果该\CurrentMacroName技术确实存在,那么也许可以起作用,但可能有不同的名称)

\documentclass{article}

%% This is my favourite (if possible)
\newcommand{\FooBar}{%

%%% Definitions and typesetting commands here...

\GenericWarning{}{Warning from \CurrentMacroName} 
%%% other stuff
}

%%% Doing it the long way

\newcommand{\OtherFooBar}{%

%%% Definitions and typesetting commands here...

\GenericWarning{}{Warning from \OtherFooBar}    
%%% other stuff
}%


\begin{document}
\FooBar%

\OtherFooBar%
\end{document}

答案1

TeX 是一种宏扩展语言:一旦宏被扩展,就没有“在”它里面的概念了。因此,没有堆栈或类似的想法,你也看不到标记“来自哪里”。(见命令可以访问其自身的名称吗?了解更多信息。

解决上述问题的常用方法是使用一个辅助内部宏来完成工作并以半自动化方式设置一切。例如,如果我们查看 LaTeX2e 定义\mathrm和这个内部帮助程序:

> \mathrm=macro:
->\protect \mathrm  .

> \mathrm =macro:
->\relax \ifmmode \else \non@alpherr \mathrm  \fi \use@mathgroup \M@OT1 \symoperators .

我们看到了\non@alpherr \mathrm,如果您没有在正确的位置输入命令,就会出现“不在数学模式”错误。

最后一步是定义命令,以便在“用户”级别仅提供一次名称。典型的构造是使用生成器命令,也许还有带有“填空”的通用错误/警告:

\documentclass{article}
\newcommand\mygenerator[2]{%
  \newcommand#1{%
    % Code here from #2
    \myerrorhelper#1%
  }%
}
\newcommand\myerrorhelper[1]{%
  \GenericWarning{}{Warning from \string#1}%
}
\mygenerator\mycommand{code}
\mygenerator\myothercommand{code}
\begin{document}
\mycommand
\myothercommand

\end{document}

请注意,由于警告不可扩展,您可以选择使用\DeclareRobustCommand其中的部分或全部。

答案2

虽然我第一次尝试通过在定义时替换标记来解决任务,但我可以展示另一个解决方案,其中\currcmd在执行时设置。你可以写:

\keepcurrcmd \newcommand \foo {... My name is \textt{\currcmd} ...}

宏命令\foo打印:...我的名字是\foo...

\keepcurrcmd宏定义\foo

\edef\currcmd{\string\foo}\foo+currcmd

而内部宏则\foo+currcmd由原来的 定义\newcommand。这种解决方案的问题在于,\foo这种方式定义的 不完全可扩展,尽管\foo不带\keepcurrcmd前缀的相同定义是可扩展的。

测试代码如下:

\documentclass{article}

\def\keepcurrcmd#1#2{%
   \def#2{%
       \edef\currcmd{\string#2}%
       \csname\string#2+currcmd\endcsname}%
   \expandafter#1\csname\string#2+currcmd\endcsname}

\keepcurrcmd \newcommand\testfirst {Text text.
   \GenericWarning{}{Warning from \currcmd}%
}
\keepcurrcmd \newcommand\testsecond [2] {Text, param #1, param #2.
   \GenericWarning{}{Warning from \currcmd}%
}

\begin{document}

Hello \testfirst,
\testsecond{paramA}{paramB}

\end{document}

答案3

这个想法是,当宏由 定义时,用 替换宏体中\thiscsname当前的。这意味着\name\mynewcommand

\mynewcommand \mynameofmacro {... My name is \string\thiscsname ...}

将被取代

\newcommand \mynameofmacro {... My name is \string\mynameofmacro ...}

我们可以这样定义 \mynewcommand:

\newtoks\macrobody
\let\newcommandOri=\newcommand
\def\mynewcommand#1#2#{%
   \def\macroname{#1}\def\macronameN{\noexpand#1}\def\newcommandparams{#2}%
   \afterassignment\mynewcommandA \macrobody
}
\def\mynewcommandA{%
   \replacemacrobody
   \edef\x{\noexpand\newcommandOri\macronameN\newcommandparams{\the\macrobody}}\x
}

宏的名称保存在 中\macroname,参数保存在 中\newcommandparams,宏主体保存在\macrobodytoks 寄存器中。宏\replacemacrobody将这个 toks 寄存器中的标记替换\thiscsname为 的内容\macroname。最后,\newcommad通过扩展 来使用具有原始 LaTeX 含义的。如果你有勇气,\x你可以这样做。\let\newcommand=\mynewcommand

这个想法的核心是替换宏,它与\macrobodytoks 寄存器一起操作。也许,您可以使用一些 LaTeX 包来执行此操作,但我不懂 LaTeX,所以我必须自己制作这个宏。这个宏必须保持所有标记(空格、、、、等)的完整性,\bgroup只执行。对不起,我对这个宏的实现有点复杂,但这有效。{}#\thiscsname -> \name

整个示例如下:

\documentclass{article}

%% The \replacemacrobody definition:
\newtoks\macrobody
\def\replacemacrobody{%
   \let\bgroup=\relax\macrobody\expandafter{\expandafter}\expandafter
   \replacemacrobodyA\the\macrobody\endmacrobody}
\def\replacemacrobodyA{\futurelet\tmp\replacemacrobodyB}
\def\replacemacrobodyB{%
   \let\next=\replacemacrobodyD
   \ifx\tmp\spacetoken \let\next=\replacemacrobodyC \addtomacrobody{ }\fi
   \ifx\tmp\thiscsname \let\next=\replacemacrobodyC
       \expandafter\addtomacrobody\macroname \fi 
   \ifx\tmp\endmacrobody \let\next=\replacemacrobodyE \fi
   \ifx\tmp\bgroupOri \let\next=\replacemacrobodyF \fi
   \next}
\def\replacemacrobodyC{\afterassignment\replacemacrobodyA \let\next= }
\long\def\replacemacrobodyD#1{\addtomacrobody#1\replacemacrobodyA}
\def\replacemacrobodyE{\ifx\IamInGroup\relax 
   \expandafter\endgroup \expandafter\macrobody\expandafter\expandafter\expandafter
      {\expandafter\the\expandafter\macrobody\expandafter{\the\macrobody}}%
   \afterassignment\replacemacrobodyA
   \else \let\bgroup=\bgroupOri \fi
   \let\next=}
\def\replacemacrobodyF{\begingroup\let\IamInGroup=\relax
   \afterassignment\replacemacrobody \macrobody=}
\long\def\addtomacrobody#1{\macrobody\expandafter{\the\macrobody#1}}
\let\bgroupOri=\bgroup
\def\tmp/{\let\spacetoken= }\tmp/ %
\def\thiscsname{^\thiscsname^}
\def\endmacrobody{^\endmacrobody^}


%% The \mynewcommand definition:
\let\newcommandOri=\newcommand
\def\mynewcommand#1#2#{%
   \def\macroname{#1}\def\macronameN{\noexpand#1}\def\newcommandparams{#2}%
   \afterassignment\mynewcommandA \macrobody
}
\def\mynewcommandA{%
   \replacemacrobody
   \edef\x{\noexpand\newcommandOri\macronameN\newcommandparams{\the\macrobody}}\x
}

\let\newcommand=\mynewcommand % I have bravery

%% The usage of redefined \newcommand:

\newcommand\testmacro
   {this is test\GenericWarning{}{Warning from \string\thiscsname}}
\newcommand\withparams [2]
   {this is \textbf{macro} with params: #1 and #2 
    \GenericWarning{}{Warning from \string\thiscsname}}

%% The test:

\begin{document}

\testmacro,
Macro \withparams {first}{second}

\end{document}

相关内容