如何测试命令的星号版本是否定义?

如何测试命令的星号版本是否定义?

这个问题类似于如何在 LaTeX 中有条件地定义新命令?但我想测试命令的星号版本是否已定义。

天真的事

\makeatletter
\@ifundefined{ref*}{}{}
\makeatother

\ref例如,对于测试的星号版本是否被定义(比如,由)它不起作用,hyperref因为它总是返回 true。

答案1

一般情况下,命令没有带星号的版本。实际上,不带星号的命令会检查第一个参数是否为星号,并相应地将控制权交给不同的辅助宏,或者直接执行两组不同的命令。

因此,随便举一个例子,我们看一下\endlargethispage并发现它的定义是:

\gdef \enlargethispage {%
   \@ifstar
     {%
      \@enlargepage{\hbox{\kern\p@}}}%
     {%
      \@enlargepage\@empty}%
}

\@ifstar是检测流浪星的关键。

从这个例子中可以清楚地看出,没有简单的测试来判断是否\enlargethispage*有效。我能想到的最精确的测试是检查\meaning\enlargethispage是否存在\@ifstar,但即使这样也无法捕获所有内容(\let\my@ifstar=\@ifstar),也可能产生误报(因为\meaning转换为字符的标记列表,因此在其中寻找精确的标记可能会被欺骗)。但这实施起来会很复杂,也许有一种更简单的方法来实现您想要实现的目标。

答案2

这基本上是不可能的,我将尝试通过一个例子来解释原因。

让我们\show\section

\section=\long macro:
-> \@startsection {section}{1}{\z@ }{-3.5ex \@plus
   -1ex \@minus -.2ex}{2.3ex \@plus .2ex}{\normalfont \Large \bfseries }

好吧,除此之外没什么可看的\section根本不接受任何争论!让我们\show\@startsection

\@startsection=macro:
#1#2#3#4#5#6->\if@noskipsec \leavevmode \fi \par \@tempskipa #4\relax \@afterindenttrue
  \ifdim \@tempskipa <\z@ \@tempskipa -\@tempskipa \@afterindentfalse \fi
  \if@nobreak \everypar {}\else \addpenalty \@secpenalty \addvspace \@tempskipa \fi
  \@ifstar {\@ssect {#3}{#4}{#5}{#6}}{\@dblarg {\@sect {#1}{#2}{#3}{#4}{#5}{#6}}}

瞧!\@ifstar定义中的\@startsection告诉你\section有一个带星号的变体。它是如何工作的?请注意\section传递给\@startsection所有 6 个强制参数,因此如果你写\section*{BLA},它就变成\@startsection{<6 arguments>}*{BLA},而这变成blablabla \@ifstar {\@ssect {#3}{#4}{#5}{#6}}{\@dblarg {\@sect {#1}{#2}{#3}{#4}{#5}{#6}}}*{BLA}。最后,\@ifstar是一个疯狂的命令,它有两个参数,如果*遵循则使用第一个参数,否则使用第二个参数。

因此,如您所见,检查命令的星号变体是否已定义确实不是一件简单的事情。更不用说\@ifstar不是定义命令的星号变体的唯一方法。问题是星号是不是命令名称的一部分, 这是第一个可选参数

相关内容