如果我重新定义带有星号变体的命令,是否总是会禁用该星号变体?

如果我重新定义带有星号变体的命令,是否总是会禁用该星号变体?

如果我重新定义带有星号变体的命令,这是否总是有效地禁用带星号的变体,作为重新定义无星号变体的副作用?

相关问题:带星号的命令会占用其后的空间吗?(其中包含的信息强烈表明“是”,但最好听到专家的确认。)

笔记:

  • 这个问题本来是一般性的。
  • 我指的是重新定义行为本身。也就是说,让我们假设我可能不会在重新定义中包含带星号的变体。
  • 我想到了一些例子(实际上用它们重新定义带星号的版本是有意义的,除非时间不够而且不需要为本地文档重新定义):
    • 我正在考虑重新定义\hspace\vspace使其可见(对于草稿编译运行)。
    • 在我早期的一些文件草稿中,\chapter被重新定义为包含对的调用\pagestyle

答案1

基本上有两种使用 *-variant 定义命令的方法。

传统方式

\newcommand{\foo}{\@ifstar\@sfoo\@foo}
\newcommand{\@sfoo}{something for the *-version}
\newcommand{\@foo}{something for the plain version}

其中\@sfoo\@foo可能有参数;当然,名称是任意的:它们是另外两个命令。会遇到许多变体;下面是一个例子:

\newcommand{\foo}{\@ifstar{\@tempswatrue\@foo}{\@tempswafalse\@foo}}
\newcommand{\@foo}{%
  \if@tempswa
     we're with the *-version
  \else
     we're with the plain version
  \fi}

\newcommand\chapter{%
   <code that's irrelevant for the example>%
   \secdef\@chapter\@schapter}

在第二个例子中,对以下 * 的查找被委托给\secdef

在许多情况下,\newcommand{\foo}{...}被改为\DeclareRobustCommand{\foo}{...}以避免在移动参数时出现问题\foo;辅助宏\@sfoo或仍然可以用(或,如果愿意的话)\@foo来定义。\newcommand\def

xparse方式

\NewDocumentCommand{\foo}{s...}
  {\IfBooleanTF{#1}
     {Code for the *-version}
     {Code for the plain version}%
  }

其中...代表其他参数类型,也可能没有。

重新定义

如果你已经\foo用其中一种方式定义了,那么

\renewcommand{\foo}{bar}

当然会消除任何\foo*像以前一样工作的可能性,因为新的\foo不会检查它后面是否有 *,而这无论如何都是 *-variant 工作的关键。

因此,如果你想用 *-variant 重新定义命令,你应该首先知道它是如何定义的。在最常见的情况下,\@ifstar在两个不同的命令之间进行选择,只需重新定义你需要的那个,所以\@foo\@sfoo。同样适用于\chapter你可以使用\@schapter或 的示例\@chapter。在这种情况下会更困难\if@tempswa,但你应该知道意图是什么。

如果你想重新定义\section,那么你会发现自己处于一个更复杂的情况,那么问题就更困难了;定义通常是根据\@startsection执行测试的宏来\@ifstar定义的,因此上面概述的方法不起作用。在这种情况下,一个简单的解决方法是说

\let\latexsection\section
\renewcommand{\section}{\@ifstar{\latexsection*}{\mynewsection}}

并针对“非*”情况进行定义\mynewsection,可能就而言\latexsection

不过要小心,一定要检查命令是如何定义的:如有疑问,\let应将其替换为\LetLtxMacro来自letltxmacro包的。

重新定义xparse基于的命令应该使用\RenewDocumentCommand适当的参数类型来完成。在这种情况下使用\let或。\LetLtxMacro


只是为了好玩,这里有一些补丁,可以使胶水插入\hspace\vspace\addvspace可见”。但是,它们不能保证总是有效并产生相同的效果。

\usepackage{regexpatch}
\makeatletter
\xpatchcmd{\@hspace}{\hskip}{\leaders\hrule\hskip}{}{}
\xpatchcmd{\@hspacer}{\hskip}{\leaders\hrule\hskip}{}{}
\xpatchcmd*{\@vspace}{\vskip#1}{\leaders\vrule\vskip#1}{}{}
\xpatchcmd*{\@vspacer}{\nobreak\vskip}{\nobreak\leaders\vrule\vskip}{}{}
\xpatchcmd{\addvspace}{\vskip}{\leaders\vrule\vskip}{}{}
\xpatchcmd*{\@xaddvskip}{\vskip\@tempskipb}{\leaders\vrule\vskip\@tempskipb}{}{}
\makeatother

答案2

这取决于您如何重新定义命令。在 TeX 级别,没有星号变体,基本命令只是向前查找星号并采取相应行动。因此,如果您重新定义它,使其仍然向前查找星号,那么它就会这样做,否则就不会。

当然,您也可以保存旧命令。

此处的\section命令使用了新的\fbox定义,但\oldsection*工作方式与以前一样。

\documentclass{article}

\let\oldsection\section

\def\section#1{\fbox{#1}}

\begin{document}

\section{abc}
One

\oldsection*{xyz}

One two

\section{zzz}
One Two three

\end{document}

或者这个版本产生相同的输出但允许您使用\section新的定义和\section*旧的星型形式。

\documentclass{article}

\let\oldsection\section

\makeatletter
\def\section{\@ifstar{\oldsection*}\fbox}
\makeatother

\begin{document}

\section{abc}
One

\section*{xyz}

One two

\section{zzz}
One Two three

\end{document}

对您添加的示例的评论。重新定义\hspace\vspace使其可见而不更改文档间距是困难的(通常可能是不可能的),因此无论您想进行何种重新定义,\@ifstar同时添加和重新定义 * 形式的额外工作都不会是您的主要问题。

book对于重新定义章节,章节的大多数定义(例如和中的定义report)已经有了,thispagestyle但忽略这一点,您可能只需将定义放在前面,而让旧定义仍然在寻找*

\let\oldchapter\chapter
\def\chapter{\cleardoublepage\thispagestyle{foo}\oldchapter}

(egreg 正确地指出,在现实生活中你不能忽视已经存在的事实,\thispagestyle因为现有的会覆盖添加的)

答案3

对于普通宏,带星号和不带星号的变体实际上是同一个宏,其中 充当*可选参数的角色;xparseegreg 描述的机制使这一点更加清晰。然而,对于环境,带星号和不带星号的变体完全不同:实际上存在称为equationequation*(来自 amsmath)的单独环境,并且重新定义equation对 完全不起作用equation*。原因是,虽然带星号的宏会执行类似

\def\macro{\@ifnextchar*{<yes>}{<no>}}

带星号的环境实际上名称中带有星号。回想一下,环境实际上是一对宏,因此:

\def\equation{...}
\def\endequation{...}

因此equation*,如果我们*把信件写成这样:

\catcode`*=11
\def\equation*{...}
\def\endequation*{...}

然而,实际操作是\begin将其参数传递给\csname...\endcsname,这意味着环境名称可能比宏名称奇怪得多:

\begin{equation*} --> \csname equation*\endcsname
\end{equation*}   --> \csname endequation*\endcsname
% plus other stuff, of course

因此,对于环境而言,加星号和不加星号的变体之间的关系是习俗代码并未强制执行这一点,而对于宏来说,实际上,带星号的变体是由扩展/解析过程创建的。

相关内容