宏只能在數學模式下定義

宏只能在數學模式下定義

我經常在數學公式中使用短宏來表示常用的符號,例如\d x微分 dx 或者,比如說,\v如果我需要一個向量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如果在 a 中找到則選擇\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-macro每個鉤子- 用於(重新)定義處理參數的巨集的套件?

問題一:

#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 .

正如您所看到的,標記保存在名稱為pattern的巨集中 。\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@postmathor時\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}

在此輸入影像描述

相關內容