
Я часто использую короткие макросы в математических формулах для часто используемых символов, например, \d x
для дифференциала dx или, скажем, \v
если мне нужен векторвочень часто в моем тексте, чтобы сделать его более читабельным. Однако, я получаю конфликт с предопределенными макросами, потому что \d
обозначает точку под следующим символом и v
ударение хачека, которое я не хочу переопределять (возможно, они мне нужны в библиографии...).
Итак, я придумал следующее, чтобы переопределить эти макросы только в математическом режиме:
\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
, если найдено в \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:
В LaTeX аргументы обозначаются символами #1
и #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
переходной \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
или требуется вызывать его несколько раз \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}