如何判断宏参数是否包含宏本身?

如何判断宏参数是否包含宏本身?

我希望有一种方法可以检测特定宏的参数是否包含该特定宏本身。考虑以下 MNWE:

\documentclass{article}
\usepackage{etoolbox}
\newcommand*{\hasself}[1]{%
  #1 \ifboolexpr{
    % what goes here?
  }{1}{0}%
}

\begin{document}
This should return `Test 1 0': \hasself{Test\hasself{}}

This should return `Test 0': \hasself{Test}
\end{document}

要使这个功能起作用,布尔测试应该是什么?或者还有其他方法可以实现相同的目标吗?

用例是 LaTeX2e,但我也愿意看到 Plain TeX、LaTeX3、LaTeX4 等解决方案。

如果可能的话,我希望解决方案可以在参数的任何分组级别上工作。如果这会产生影响,您可以假设参数不会\long(所以我想我真的可以\newcommand*在 MNWE 中使用:已更新)。对于我的使用来说,没有必要捕捉等效\def宏,但如果有可能的话,看到它会很好。

答案1

由于countersLaTeX 中是全局量,我决定使用它们来解决您的问题。这并不能完全满足您的要求,因为它先打印最内层的结果,然后再打印嵌套中最外层的结果。这意味着,对于您的第一个示例,输出是“0 1”而不是“1 0”。但我认为,对于您想要使用它的目的,这可能是可以接受的。

输出实际上是递归的级别。

\documentclass{article}
\usepackage{etoolbox}
\newcounter{hcount}
\newcommand*{\hasself}[1]{%
  \addtocounter{hcount}{-1}%
  \edef\hbefore{\thehcount}%
  #1\ %
  \the\numexpr\thehcount-\hbefore\relax%
  \addtocounter{hcount}{1}%
}
\def\junk#1{\hasself{#1}}

\begin{document}
This should return `Test 1 0': \hasself{Test\hasself{}}

This should return `Test 0': \hasself{Test}

\hasself{Test\hasself{Test\hasself{}}}

\hasself{Test\junk{Test\hasself{}}}
\end{document}

在此处输入图片描述

答案2

Steven 的解决方案基于参数被处理的事实。我在这里展示了另一种解决方案:参数仅逐个标记进行扫描。该解决方案基于以下代码:OPmac 技巧 www 页面。这里只需添加几行即可实现实际任务。

%% the code from http://petr.olsak.net/opmac-tricks-e.html#readtoks :

\newtoks\readtoksT \newif\ifreadtoksG
\def\readtoks{\begingroup \let\bgroup=\relax \let\egroup=\relax
   \readtoksT={}\readtoksGfalse \afterassignment\readtoksA \let\next= }
\def\readtoksA{\futurelet\tmpc\readtoksB}
\def\readtoksB{\let\next=\readtoksD \csname readtoksX\endcsname
   \ifcat\space\noexpand\tmpc \let\next=\readtoksC \def\nexxt{\readtoksD{ }}\fi
   \ifcat{\noexpand\tmpc      \let\next=\readtoksC \let\nexxt=\readtoksE \fi
   \ifcat}\noexpand\tmpc      \let\next=\readtoksC \let\nexxt=\readtoksF \fi
   \next
}
\def\readtoksC{\afterassignment\nexxt \let\next= }
\def\readtoksD#1{\readtoksT=\expandafter{\the\readtoksT#1}\readtoksA}
\def\readtoksE{\begingroup \readtoksGtrue \readtoksT={}\readtoksA}
\def\readtoksF{\ifreadtoksG
   \expandafter\endgroup\expandafter\readtoksT\expandafter\expandafter\expandafter
      {\expandafter\the\expandafter\readtoksT\expandafter{\the\readtoksT}}%
      \expandafter\readtoksA
   \else
      \expandafter\endgroup\expandafter\readtoksO\expandafter{\the\readtoksT}%
   \fi
}
\def\readtoksO{\toks1}

%% The code for this actual task:

\newif\iftokenfound
\def\testtoken#1#2{{\global\tokenfoundfalse
   \def\readtoksX{\ifx\tmpc#1\global\tokenfoundtrue\fi}%
   \readtoks{#2}% 
}}

\def\hasself#1{\testtoken\hasself{#1}%   
   \iftokenfound Yes, hasself is present.
   \else         hasself isn't present.
   \fi
}

%%% Test:

NO:   \hasself{Normal {text}}

YES:  \hasself{Special {text \hasself{in group}}}

\bye

相关内容