
A menudo uso macros cortas en fórmulas matemáticas para símbolos de uso frecuente, por ejemplo, \d x
para un dx diferencial o, por ejemplo, \v
si necesito un vector.vmuy a menudo en mi texto para hacer las cosas más legibles. Sin embargo, tengo un conflicto con las macros predefinidas porque \d
representan un punto debajo del siguiente carácter y v
el acento hacek, que no quiero anular (tal vez los necesite en la bibliografía...).
Entonces, se me ocurrió lo siguiente para anular esas macros solo en 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}
Sin embargo, me gustaría que el comando también se defina como macros normales, es decir \mathdef{\d}{\mathrm{d}}
, y que también pueda aceptar argumentos, pero todos mis experimentos con expandafter
, unexpanded
etc. no funcionaron y solo generaron los extraños mensajes de error habituales. ¿Alguna pista de cómo puedo hacer eso?
Además, ¿cree que existe una gran penalización en el rendimiento si se usa \everymath
así en un documento grande?
Respuesta1
Mi idea es muy similar a la de egreg, pero me gustaría agregar un argumento opcional, para que el comando matemático pueda procesar los argumentos por sí mismo. El 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}
El resultado:
Respuesta2
Yo no lo usaría \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}
Sin embargo, no creo que este sea un buen camino a seguir. Es confuso y propenso a errores.
Ver¿Cuándo utilizar \LetLtxMacro?para obtener información sobre \LetLtxMacro
.
Es un poco más fácil con 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 qué uso \DeclareRobustCommand
(primera versión) o \protected
(segunda versión) en todas partes?
Cuando TeX realiza una operación de escritura, no está en ningún modo, en particular no está en modo matemático. Una definición simplista como
\newcommand{\foo}{%
\relax % if this comes first in an alignment
\ifmmode
\expandafter\@firstoftwo
\else
\expandafter\@secondoftwo
\fi
\foo@math\foo@text
}
haríasiempreelija \foo@text
si se encuentra en un \write
. Las protecciones anteriores hacen que los \mathdef
comandos se escriban solos, por lo que no se realiza ninguna elección en ese \write
momento.
¿Por qué no usar \everymath
? Pruebe el siguiente ejemplo y verá.
\documentclass{article}
\everymath{\let\foo\foomath}
\newcommand{\foo}{Foo}
\newcommand{\foomath}{Ops}
\begin{document}
Text: \foo
Math: $\foo$
Ops: abc\textsuperscript{\foo}
\end{document}
Respuesta3
Desea utilizar la \PushPostHook
-macro delcada gancho-¿Paquete para (re)definir macros que procesan argumentos?
Número 1:
En LaTeX los argumentos se denotan por #1
y #2
similares, es decir, por secuencias de hashes seguidas por un dígito en el rango 1...9.
Cuando se trata de anidar definiciones, es necesario duplicar los hashes para las definiciones internas:
\def\outsidemacro#1{%
\def\insidemacro##1{This is insidemacro's argument: ##1.}%
This is outsidemacro's argument: #1.%
}
Durante la expansión, dos hashes consecutivos se colapsarán en un solo hash, es decir, la cantidad de hashes se reducirá a la mitad.
Es decir, \outsidemacro{foo}
produce:
\def\insidemacro#1{This is insidemacro's argument: #1.}%
This is outsidemacro's argument: foo.%
Mirando la definición de \PushPostHook
vía \show\PushPostHook
produce:
> \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 puedes ver, los tokens se guardan en macros cuyos nombres son de patrón .\eh@post⟨name of hook⟩
Por ejemplo, \eh@postmath
y \eh@postdisplay
.
Para agregar tokens a dicha macro, \PushPostHook
haga que la macro en cuestión sea igual \eh@tempi
y luego redefina la macro en cuestión agregando los tokens en cuestión detrás deexpansiónde \eh@tempi
.
La expansión \eh@tempi
es un punto crucial:
En caso de que la definición \eh@tempi
contenga hashes ( #
), durante la expansión, dos hashes consecutivos colapsarán en uno de ellos.
Esto implica:
Siempre que \PushPostHook
se solicite agregar cosas a \eh@postmath
o \eh@postdisplay
, la cantidad de hashes consecutivos con cosas que ya están \eh@postmath
o \eh@postdisplay
que estarán dentro se reducirá a la mitad.
Esto será un problema, especialmente cuando llame \PushPostHook
varias veces para agregar cosas a \eh@postmath
o \eh@postdisplay
.
Puede evitar reducir a la mitad la cantidad de hashes consecutivos manteniendo las cosas mediante un registro de token porque cuando el contenido de un registro de token se "escupe" a través de \the
, la cantidad de hashes no se reducirá. Cuando \the
se realiza el "escupimiento" debido a \edef
, la cantidad de hashes no solo no se reducirá sino que se duplicará.
Si lo hace, por ejemplo,
\myscratchtoks{#}
\edef\mymacro{\the\myscratchtoks}
, en la definición, \mymacro
la cantidad de hashes provenientes \myscratchtoks
se duplicará. Al expandirse \mymacro
, esa cantidad duplicada se reducirá a la mitad y, por lo tanto, la expansión de \mymacro
entrega la misma cantidad de hashes que entregaría \the\myscratchtoks
.
Por lo tanto, sugiero agregar cosas a un registro de token y usarlas \PushPostHook
solo para "vaciar" ese registro de token.
Número 2:
Si desea mantener macros que también puedan procesar argumentos, le sugiero implementar algo que pueda usarse de manera similar a los prefijos \long
o \global
.
En el siguiente ejemplo utilicé #{
la notación -para implementar una macro \mathcommand
que procesa argumentos delimitados por llaves a la izquierda que van seguidos de argumentos anidados entre llaves. Como ⟨definition text⟩
una macro siempre debe anidarse entre llaves, puede utilizar el procesamiento de argumentos delimitados por llaves izquierdas para recuperar todos los tokens que vienen antes de un archivo ⟨definition text⟩
.
Dichos tokens pueden ser el comando de definición en sí (p. ej., \renewcommand*
o \global\long\def
), el token de secuencia de control que se va a (re)definir y el ⟨parameter text⟩
.
Por ejemplo, con
\mathcommand\renewcommand*\mymacrowitharguments[2]{%
\mbox{math-arg~1: }(#1)
\mbox{ math-arg~2: }(#2)
}%
, \mathcommand
el primer argumento (delimitado por llaves a la izquierda) será la secuencia \renewcommand*\mymacrowitharguments[2]
y su segundo argumento estará formado por el material que está anidado dentro de las llaves: \mbox{math-arg~1: }(#1) \mbox{ math-arg~2: }(#2)
.
\mathcommand
agregará la secuencia , es decir, la secuencia al registro de token .⟨first argument⟩{⟨second argument⟩}
\renewcommand*\mymacrowitharguments[2]{\mbox{math-arg~1: }(#1) \mbox{ math-arg~2: }(#2) }
\mymathhooktoks
También implementé una macro \mathcommandfromname
que también recupera un argumento delimitado por llaves izquierdas y toma el material que está detrás de ese argumento y que, por lo tanto, está anidado entre llaves para el nombre de un token de secuencia de control que se puede formar mediante \csname..\endcsname
:
P.ej,
\mathcommandfromname\renewcommand*{mymacrowitharguments}[2]{%
\mbox{math-arg~1: }(#1)
\mbox{ math-arg~2: }(#2)
}%
rendimientos:
\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}