
Costumo usar macros curtas em fórmulas matemáticas para símbolos usados com frequência, por exemplo, \d x
para um dx diferencial ou, digamos, \v
se eu precisar de um vetorvmuitas vezes no meu texto para tornar as coisas mais legíveis. No entanto, tenho um conflito com macros predefinidas porque \d
representa um ponto sob o próximo caractere e v
o acento hacek, que não quero substituir (talvez eu precise deles na bibliografia...).
Então, criei o seguinte para substituir essas macros apenas no modo matemático:
\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}
No entanto, eu gostaria que o comando também fosse definido como macros normais, ou seja \mathdef{\d}{\mathrm{d}}
, e também pudesse receber argumentos, mas todos os meus experimentos com expandafter
, unexpanded
, etc. não funcionaram e levaram apenas às estranhas mensagens de erro usuais. Alguma dica de como posso fazer isso?
Além disso, você acha que há uma grande penalidade no desempenho ao usar \everymath
esse tipo de documento em um documento grande?
Responder1
Minha ideia é muito semelhante à do egreg, mas gostaria de adicionar um argumento opcional, para que o comando matemático possa processar os próprios argumentos. O código:
\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}
O resultado:
Responder2
eu não usaria \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}
Eu não acho que este seja um bom caminho a percorrer, no entanto. É confuso e propenso a erros.
VerQuando usar \LetLtxMacro?para obter informações sobre \LetLtxMacro
.
É um pouco mais fácil com 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}
Por que uso \DeclareRobustCommand
(primeira versão) ou \protected
(segunda versão) por aí?
Quando o TeX faz uma operação de gravação, ele não está em nenhum modo, em particular, não está no modo matemático. Uma definição simplista como
\newcommand{\foo}{%
\relax % if this comes first in an alignment
\ifmmode
\expandafter\@firstoftwo
\else
\expandafter\@secondoftwo
\fi
\foo@math\foo@text
}
seriasempreescolha \foo@text
se for encontrado em um arquivo \write
. As proteções acima fazem com que os \mathdef
comandos se escrevam sozinhos, portanto nenhuma escolha é feita na \write
hora.
Por que não usar \everymath
? Experimente o exemplo a seguir e veja.
\documentclass{article}
\everymath{\let\foo\foomath}
\newcommand{\foo}{Foo}
\newcommand{\foomath}{Ops}
\begin{document}
Text: \foo
Math: $\foo$
Ops: abc\textsuperscript{\foo}
\end{document}
Responder3
Você deseja usar o \PushPostHook
-macro dotodos os ganchos-pacote para (re)definir macros que processam argumentos?
Problema 1:
No LaTeX os argumentos são denotados por #1
e #2
e similares, ou seja, por sequências de hashes seguidas por um dígito no intervalo 1..9.
Quando se trata de aninhar definições, os hashes precisam ser duplicados para as definições internas:
\def\outsidemacro#1{%
\def\insidemacro##1{This is insidemacro's argument: ##1.}%
This is outsidemacro's argument: #1.%
}
Durante a expansão, dois hashes consecutivos serão reunidos em um hash, ou seja, a quantidade de hashes será reduzida à metade.
Ou seja, \outsidemacro{foo}
rende:
\def\insidemacro#1{This is insidemacro's argument: #1.}%
This is outsidemacro's argument: foo.%
Olhando para a definição de rendimentos \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 .
Como você pode ver, os tokens são mantidos em macros cujos nomes são de padrão .\eh@post⟨name of hook⟩
Por exemplo, \eh@postmath
e \eh@postdisplay
.
Para adicionar tokens a tal macro, \PushPostHook
permite que a macro em questão seja igual \eh@tempi
e redefina a macro em questão anexando os tokens em questão atrás doexpansãode \eh@tempi
.
Expandir \eh@tempi
é um ponto crucial:
Caso a definição de \eh@tempi
contenha hashes ( #
), durante a expansão dois hashes consecutivos serão colapsados em um deles.
Isso implica:
Sempre que \PushPostHook
for necessário adicionar coisas a \eh@postmath
ou \eh@postdisplay
, a quantidade de hashes consecutivos com coisas que já estão \eh@postmath
ou \eh@postdisplay
serão reduzidas pela metade.
Isso será um problema, especialmente ao ligar \PushPostHook
várias vezes para adicionar coisas a \eh@postmath
ou \eh@postdisplay
.
Você pode evitar a redução pela metade da quantidade de hashes consecutivos mantendo as coisas por meio de um registro de token, porque quando o conteúdo de um registro de token é "cuspido" via \the
, a quantidade de hashes não será reduzida. Quando "cuspir" devido a \the
ocorre durante \edef
, a quantidade de hashes não apenas não será reduzida, mas também será duplicada.
Se você fizer isso, por exemplo,
\myscratchtoks{#}
\edef\mymacro{\the\myscratchtoks}
, na definição da \mymacro
quantidade de hashes provenientes \myscratchtoks
será duplicada. Ao expandir \mymacro
, essa quantidade duplicada será reduzida pela metade e, portanto, a expansão \mymacro
entregará a mesma quantidade de hashes que seria entregue por \the\myscratchtoks
.
Portanto, sugiro adicionar coisas a um registro de token e usá-lo \PushPostHook
apenas para "liberar" esse registro de token.
Edição 2:
Se você deseja manter macros que também possam processar argumentos, sugiro implementar algo que possa ser usado de forma semelhante aos prefixos \long
ou \global
.
No exemplo abaixo, usei #{
-notation para implementar uma macro \mathcommand
que processa argumentos delimitados por chaves à esquerda que são seguidos por argumentos aninhados por chaves. Como o ⟨definition text⟩
de uma macro sempre deve ser aninhado entre colchetes, você pode usar o processamento de argumentos delimitados por colchetes à esquerda para buscar todos os tokens que vêm antes de a ⟨definition text⟩
.
Esses tokens podem ser o próprio comando de definição (por exemplo, \renewcommand*
ou \global\long\def
), o token de sequência de controle que deve ser (re)definido e o ⟨parameter text⟩
.
Por exemplo, com
\mathcommand\renewcommand*\mymacrowitharguments[2]{%
\mbox{math-arg~1: }(#1)
\mbox{ math-arg~2: }(#2)
}%
, \mathcommand
o primeiro argumento (delimitado por colchetes à esquerda) de será a sequência \renewcommand*\mymacrowitharguments[2]
e seu segundo argumento é formado pelo material que está aninhado entre colchetes: \mbox{math-arg~1: }(#1) \mbox{ math-arg~2: }(#2)
.
\mathcommand
adicionará a sequência , ou seja, a sequência ao token-register .⟨first argument⟩{⟨second argument⟩}
\renewcommand*\mymacrowitharguments[2]{\mbox{math-arg~1: }(#1) \mbox{ math-arg~2: }(#2) }
\mymathhooktoks
Eu também implementei uma macro \mathcommandfromname
que também busca um argumento delimitado por chaves à esquerda e pega o material que está por trás desse argumento e que, portanto, está aninhado entre chaves para o nome de um token de sequência de controle que pode ser formado por meio de \csname..\endcsname
:
Por exemplo,
\mathcommandfromname\renewcommand*{mymacrowitharguments}[2]{%
\mbox{math-arg~1: }(#1)
\mbox{ math-arg~2: }(#2)
}%
rendimentos:
\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}