宏只能在数学模式下定义

宏只能在数学模式下定义

我经常在数学公式中使用简短的宏来表示常用的符号,例如\d x微分 dx,或者\v如果我需要一个向量在我的文本中经常使用,以使内容更具可读性。但是,我遇到了与预定义宏的冲突,因为\d代表下一个字符下的点和vhacek 重音,我不想覆盖它(也许我在参考书目中需要它们......)。

因此,我想出了以下内容,仅在数学模式下覆盖这些宏:

\documentclass{article}

\usepackage{everyhook}
\newcommand{\mathdef}[2]{%
  \PushPostHook{math}{\expandafter\def\csname #1\endcsname{#2}}%
  \PushPostHook{display}{\expandafter\def\csname #1\endcsname{#2}}}
\mathdef{d}{\mathrm{d}}%

\begin{document}
  \d x  is an x with a dot below and $\int f(x) \d x$ is an integral over $x$.
\end{document}

但是,我希望命令也能像普通宏一样定义,即\mathdef{\d}{\mathrm{d}},并且能够接受参数,但我对expandafterunexpanded等的所有实验都没有成功,只导致了常见的奇怪错误消息。有什么提示我该怎么做吗?

\everymath此外,您是否认为在大型文档中使用这种方法会造成很大的性能损失?

答案1

我的想法和 egreg 的非常相似,但我想添加一个可选参数,这样 math 命令就可以自己处理参数。代码:

\documentclass{article}

\usepackage{xparse}
\DeclareDocumentCommand{\mathdef}{mO{0}m}{%
  \expandafter\let\csname old\string#1\endcsname=#1
  \expandafter\newcommand\csname new\string#1\endcsname[#2]{#3}
  \DeclareRobustCommand#1{%
    \ifmmode
      \expandafter\let\expandafter\next\csname new\string#1\endcsname
    \else
      \expandafter\let\expandafter\next\csname old\string#1\endcsname
    \fi
    \next
  }%
}

\mathdef{\v}[1]{\tilde{#1}}
\mathdef{\d}{\mathrm{d}}

\begin{document}
Ha\v{c}ek and tilde $\v{a}+\v{b}=1$.

\d x  is an x with a dot below and $\int f(x) \d x$ is an integral over $x$.
\end{document}

结果:

在此处输入图片描述

答案2

我不会用\everymath

\documentclass{article}
\usepackage{letltxmacro}

\makeatletter
\newcommand{\mathdef}[2]{%
  \@ifundefined{#1}{\@mathdef@new{#1}{#2}}{\@mathdef@remember{#1}{#2}}
}

\newcommand{\@mathdef@remember}[2]{%
  \expandafter\LetLtxMacro
    \csname textmode@#1\expandafter\endcsname
    \csname #1\endcsname
  \expandafter\def\csname mathmode@#1\endcsname{#2}%
  \expandafter\DeclareRobustCommand\csname#1\endcsname{%
    \ifmmode\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
     {\csname mathmode@#1\endcsname}{\csname textmode@#1\endcsname}%
  }%
}
\newcommand{\@mathdef@new}[2]{%
  \expandafter\DeclareRobustCommand\csname#1\endcsname{#2}%
}
\makeatother

\mathdef{d}{\mathop{}\!\mathrm{d}}

\begin{document}

\d{x} is an x with a dot below and $\int f(x) \d x$ is an integral over $x$.

\end{document}

在此处输入图片描述

但我不认为这是个好办法。它容易让人困惑,而且容易出错。

何时使用 \LetLtxMacro?了解详情\LetLtxMacro

使用起来更简单一些etoolbox

\documentclass{article}
\usepackage{etoolbox}

\makeatletter
\newcommand{\mathdef}[2]{%
  \@ifundefined{#1}{\@mathdef@new{#1}{#2}}{\@mathdef@remember{#1}{#2}}
}

\newcommand{\@mathdef@remember}[2]{%
  \expandafter\robustify\csname#1\endcsname
  \csletcs{textmode@#1}{#1}%
  \csdef{mathmode@#1}{#2}%
  \protected\csdef{#1}{%
    \ifmmode\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
     {\csuse{mathmode@#1}}{\csuse{textmode@#1}}%
  }%
}
\newcommand{\@mathdef@new}[2]{%
  \protected\csdef{#1}{#2}%
}
\makeatother

\mathdef{d}{\mathop{}\!\mathrm{d}}

\begin{document}

\d{x} is an x with a dot below and $\int f(x) \d x$ is an integral over $x$.

\end{document}

为什么我到处都使用\DeclareRobustCommand(第一个版本)或(第二个版本)?\protected

当 TeX 执行写入操作时,它根本不处于任何模式,特别是它不处于数学模式。一个简单的定义,例如

\newcommand{\foo}{%
  \relax % if this comes first in an alignment
  \ifmmode
    \expandafter\@firstoftwo
  \else
    \expandafter\@secondoftwo
  \fi
  \foo@math\foo@text
}

总是\foo@text如果在 中找到,则选择\write。上述保护使得\mathdef命令可以自行写入,因此此时无需做出选择\write

为什么不使用\everymath?试试下面的例子看看。

\documentclass{article}

\everymath{\let\foo\foomath}
\newcommand{\foo}{Foo}
\newcommand{\foomath}{Ops}

\begin{document}

Text: \foo

Math: $\foo$

Ops: abc\textsuperscript{\foo}

\end{document}

答案3

您希望使用\PushPostHook来自一切钩子-用于(重新)定义处理参数的宏的包?

问题 1:

#1在 LaTeX 中,参数用和之类的符号表示#2,即用井号序列加上 1..9 范围内的数字表示。

当涉及嵌套定义时,内部定义的哈希值需要加倍:

\def\outsidemacro#1{%
  \def\insidemacro##1{This is insidemacro's argument: ##1.}%  
  This is outsidemacro's argument: #1.%
}

在扩展时,两个连续的哈希值将被折叠成一个哈希值,即哈希值的数量将减半。

即,\outsidemacro{foo}得到:

\def\insidemacro#1{This is insidemacro's argument: #1.}%  
This is outsidemacro's argument: foo.%

\PushPostHook查看via产量的定义\show\PushPostHook

> \PushPostHook=\protected\long macro:
#1#2->\eh@checkhook {#1}\PushPostHook \letcs \eh@tempi {eh@post#1}\expandafter 
\gdef \csname eh@post#1\expandafter \endcsname \expandafter {\eh@tempi \eh@hook
separator #2}\undef \eh@tempi .

如您所见,标记保存在名称为模式的宏中 。\eh@post⟨name of hook⟩

例如,\eh@postmath\eh@postdisplay

为了向这样的宏添加标记,\PushPostHook让相关宏等于\eh@tempi,然后通过将相关标记附加到扩张\eh@tempi

扩展\eh@tempi是关键点:

如果的定义\eh@tempi包含哈希值(#),则在扩展过程中两个连续的哈希值将折叠为其中一个。

这意味着:

每当\PushPostHook调用 向\eh@postmath或中添加事物时,已存在或 的\eh@postdisplay连续哈希值的数量将减半。\eh@postmath\eh@postdisplay

这会成为一个问题,尤其是当\PushPostHook多次调用以将内容添加到\eh@postmath或时\eh@postdisplay

通过使用令牌寄存器来维护事物,可以避免连续哈希值数量减半,因为当令牌寄存器的内容通过 被“吐出”时\the,哈希值数量不会减少。当\the在 期间由于 而发生“吐出”时\edef,哈希值数量不仅不会减少,而且会加倍。

如果你这样做,例如,

\myscratchtoks{#}
\edef\mymacro{\the\myscratchtoks}

,在 的定义中,\mymacro来自 的哈希值数量\myscratchtoks将加倍。当扩展 时\mymacro,加倍的数量将减半,因此 的扩展将\mymacro提供与 相同的哈希值\the\myscratchtoks

因此,我建议将内容添加到令牌寄存器中,并 \PushPostHook仅用于“刷新”该令牌寄存器。

问题 2:

如果您希望维护也可以处理参数的宏,我建议实现一些类似于前缀\long或的东西\global

在下面的例子中,我使用#{-notation 来实现一个宏\mathcommand,该宏处理由括号​​嵌套参数后面的左括号分隔参数。由于⟨definition text⟩宏的 总是嵌套在括号中,因此您可以使用左括号分隔参数的处理来获取 之前的所有标记⟨definition text⟩
这些标记可以是定义命令本身(例如\renewcommand*\global\long\def)、要(重新)定义的控制序列标记和⟨parameter text⟩

例如,

\mathcommand\renewcommand*\mymacrowitharguments[2]{%
  \mbox{math-arg~1: }(#1) 
  \mbox{ math-arg~2: }(#2)
}%

,\mathcommand的第一个(左括号分隔的)参数将是序列\renewcommand*\mymacrowitharguments[2],而它的第二个参数由嵌套在括号内的内容组成:\mbox{math-arg~1: }(#1) \mbox{ math-arg~2: }(#2).
\mathcommand将添加序列,即将序列添加到标记寄存器。⟨first argument⟩{⟨second argument⟩}\renewcommand*\mymacrowitharguments[2]{\mbox{math-arg~1: }(#1) \mbox{ math-arg~2: }(#2) }\mymathhooktoks

我还实现了一个宏\mathcommandfromname,它还获取一个左括号分隔的参数并获取该参数后面的内容,因此嵌套在括号中,以获得可以通过以下方式形成的控制序列标记的名称\csname..\endcsname

例如,

\mathcommandfromname\renewcommand*{mymacrowitharguments}[2]{%
  \mbox{math-arg~1: }(#1) 
  \mbox{ math-arg~2: }(#2)
}%

产量:

\mathcommand\renewcommand*\mymacrowitharguments[2]{%
  \mbox{math-arg~1: }(#1) 
  \mbox{ math-arg~2: }(#2)
}%

\documentclass{article}

\usepackage{everyhook}

\newtoks\myscratchtoks
\newcommand\mymathhookmacro{}%

\PushPostHook{math}{\mymathhookmacro}%
\PushPostHook{display}{\mymathhookmacro}%

\newcommand{\mathcommand}{}%
\long\def\mathcommand#1#{\innermathcommand{#1}}%
\newcommand{\innermathcommand}[2]{%
  \begingroup
  \expandafter\myscratchtoks\expandafter{\mymathhookmacro#1{#2}}%
  \xdef\mymathhookmacro{\the\myscratchtoks}%
  \endgroup
}

\newcommand\exchange[2]{#2#1}%
\newcommand\mathcommandfromname{}%
\long\def\mathcommandfromname#1#{\romannumeral0\innermathcommandfromname{#1}}%
\newcommand\innermathcommandfromname[2]{%
  \expandafter\exchange\expandafter{\csname#2\endcsname}{ \mathcommand#1}%
}%

%----------------------------------------------------------------

\newcommand*\mymacrowitharguments[2]{%
  non-math-arg~1: \textbf{(#1)} %
  non-math-arg~2: \textbf{(#2)}%
}%

\mathcommand\renewcommand*\mymacrowitharguments[2]{%
  \mbox{math-arg~1: }(#1)
  \mbox{ math-arg~2: }(#2)
}%

\newcommand*\myothermacrowitharguments[2]{%
  other-non-math-arg~1: \textbf{(#1)} %
  other-non-math-arg~2: \textbf{(#2)}%
}%

\mathcommandfromname\renewcommand*{myothermacrowitharguments}[2]{%
  \mbox{other-math-arg~1: }(#1)
  \mbox{ other-math-arg~2: }(#2)
}%

\mathcommand\renewcommand*\d{\mathrm{d}}%

\parindent=0ex

\begin{document}

\d x  is an x with a dot below and $\int f(x) \d x$ is an 
integral over $x$.

\bigskip

Testing with \verb|\mymacrowitharguments|:\smallskip

outside math:\\
\mymacrowitharguments{arg A}{arg B}
\smallskip

inside math:\\
$\mymacrowitharguments{arg A}{arg B}$
\bigskip

Testing with \verb|\myothermacrowitharguments|:\smallskip

outside math:\\
\myothermacrowitharguments{arg A}{arg B}
\smallskip

inside math:\\
$\myothermacrowitharguments{arg A}{arg B}$

\end{document}

在此处输入图片描述

相关内容