检查宏是否在另一个宏内出现

检查宏是否在另一个宏内出现

当然,使用substr(或) 包,您可以识别控制序列是否包含某些文本字符串:xstring

\documentclass{article}
\usepackage{substr}

\def\somecontrolseq{some silly text}

\begin{document}
\IfSubStringInString{silly}{\somecontrolseq}
  {true}
  {false}
\end{document}

但是,如果\somecontrolseq您想要检查其自身的控制序列,就像下面这个不起作用的例子那样,该怎么办呢?

\documentclass{article}
\usepackage{substr}

\def\somecontrolseq{\macro{silly} text}
\def\macro#1{some #1}

\begin{document}
\IfSubStringInString{\detokenize{\macro}}{\somecontrolseq}
  {true}
  {false}
\end{document}

\somecontrolseq我发现有关内容和的 catcode\detokenize{\macro}以及(可能更重要的是)的扩展存在问题\somecontrolseq

答案1

幸运的是,我发现 OP 中提到的两个问题实际上是唯一的问题,并且在尝试了许多其他方法之后,可以直接解决它们 :)

扩展问题通过对\expandafter结果进行简单的“之前”去标记化(即\detokenize\expandafter{\somecontrolseq})来解决,这再次解决了 catcode 的问题,因为我搜索的子字符串和比较的字符串现在都有 catcode 12。

这个例子

\documentclass{article}
\usepackage{substr}

\def\somecontrolseq{\macro{silly} text}
\def\macro#1{some #1}

\begin{document}
\IfSubStringInString{\detokenize{\macro}}{\detokenize\expandafter{\somecontrolseq}}
  {true}
  {false}
\end{document}

编译后会变成

真的

按要求。

答案2

我添加这个答案只是为了证明我的断言,即当前的问题“在某种程度上与”如何检查两个控制序列是否具有相同的名称?(见评论)。

沃纳的解决方案很棒,但它有一个小缺点和一个特点,根据你想做什么,它可能是一个有用的功能,也可能不是一个有用的功能。

您可能想要禁用的有用功能是它可以在任何扩展级别找到所寻求的控制序列;缺点是测试不是纯粹可扩展的,因为它是在胃中执行的。

以下方法使用两个步骤:第一步需要消化(因为它需要执行赋值),定义要搜索的名称;完成此操作后(这是第二步),可以执行纯扩展测试以查明另一个宏的替换文本是否包含之前声明的控制序列。测试不会进一步扩展替换文本(即仅考虑一个扩展级别)。

\documentclass{article}

\makeatletter

% Check for availability of names:
\@ifdefinable\MacroDoesNotContainSavedName{}
\@ifdefinable\@MacroDoesNotContainSavedName{}

\newcommand*\DefineMacroNameToBeSearched[1]{%
  \def\@MacroDoesNotContainSavedName##1#1##2\@@@{%
    \ifx\@empty##2\@empty % if ##2 is empty
  }%
  \def\MacroDoesNotContainSavedName##1{%
    TT\fi
    \expandafter\@MacroDoesNotContainSavedName##1#1\@@@
  }%
}
% \newcommand*\checkdefinitions{%
%   \show\MacroDoesNotContainSavedName
%   \show\@MacroDoesNotContainSavedName
% }

\makeatother

\newcommand*{\DonaldDuck}{Quack!}
\newcommand*{\Duckburg}{%
  Ducks living in Duckburg: \UncleScrooge, \DonaldDuck,
  \Huey, \Dewey, \Louie, and many others.%
}
\newcommand*{\Micetown}{%
  Mices living in Micetown: \MickeyMouse, \MinnieMouse,~\ldots
}
\newcommand*{\MinnieMouse}{%
  I'm secretly in love with \DonalDuck!%
}



\begin{document}

\DefineMacroNameToBeSearched{\DonaldDuck}
% \DefineMacroNameToBeSearched{\MickeyMouse}
% \DefineMacroNameToBeSearched{\Huey}
% \checkdefinitions

Test for Duckburg: the macro
\if\MacroDoesNotContainSavedName{\Duckburg}%
  DOESN'T CONTAIN%
\else
  CONTAINS%
\fi
\ the saved name.

Test for Micetown: the macro
\if\MacroDoesNotContainSavedName{\Micetown}%
  DOESN'T CONTAIN%
\else
  CONTAINS%
\fi
\ the saved name.

\end{document}

您可以取消注释此代码的各个部分以查看不同的行为。一些注释掉的代码用于诊断目的。

此代码仅需要原始的 Knuth TeX。测试完全独立于所搜索的控制序列的含义和类型(宏、原始命令等),这些控制序列也可能未定义。缺点:您无法可靠地测试是否存在名为“ \@empty”的宏;这可以通过通常的方式解决\detokenize(不会破坏测试纯粹可扩展的属性)。

答案3

如果包含宏没有执行任何特殊操作(即不会在组中存在),您可以将内容设置在框中。这样您就可以测试某些内容,而无需打印任何内容:

真假

\documentclass{article}

\def\somecontrolseq{\macro{silly} text}
\def\macro#1{some #1}

\newif\ifmacroexists
% \testifcontainsmacro{<macro>}{<another macro>}
\newcommand{\testifcontainsmacro}[2]{{
  \macroexistsfalse
  \def#1{\global\macroexiststrue}%
  \setbox0=\hbox{#2}%
  \ifmacroexists
    true
  \else
    false
  \fi
}}

\begin{document}

\testifcontainsmacro{\macro}{\somecontrolseq}

\testifcontainsmacro{\Macro}{\somecontrolseq}

\end{document}

这个想法是重新定义所寻求的宏来设置布尔标志,之后您将容器宏设置在框内(您永远不会使用)。如果已设置标志,则宏存在于容器内(无论扩展级别如何),否则不存在。

分组会将\testifcontainsmacro任何设置恢复为其原始值,除非该设置注定会在组中存活下来。

当然,一个缺点是如果你只想测试(比如说)第一次扩展,而不是后续扩展......

相关内容