Макрос можно определить только в математическом режиме

Макрос можно определить только в математическом режиме

Я часто использую короткие макросы в математических формулах для часто используемых символов, например, \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}

введите описание изображения здесь

Связанный контент