
我經常在數學公式中使用短宏來表示常用的符號,例如\d x
微分 dx 或者,比如說,\v
如果我需要一個向量v在我的文本中經常出現,以使內容更具可讀性。但是,我與預定義的巨集發生衝突,因為\d
代表下一個字元下的一個點和v
hacek 重音,我不想覆蓋它(也許我在參考書目中需要它們...)。
因此,我想出了以下方法來僅在數學模式下覆寫這些巨集:
\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}}
,並且也能夠接受參數,但我對expandafter
、unexpanded
等的所有實驗都沒有成功,只導致常見的奇怪錯誤訊息。有什麼提示我該怎麼做嗎?
\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@postmath
or時\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}