Macro solo para definir en modo matemático

Macro solo para definir en modo matemático

A menudo uso macros cortas en fórmulas matemáticas para símbolos de uso frecuente, por ejemplo, \d xpara un dx diferencial o, por ejemplo, \vsi 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 \drepresentan un punto debajo del siguiente carácter y vel 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, unexpandedetc. 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 \everymathasí 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:

ingrese la descripción de la imagen aquí

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}

ingrese la descripción de la imagen aquí

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@textsi se encuentra en un \write. Las protecciones anteriores hacen que los \mathdefcomandos se escriban solos, por lo que no se realiza ninguna elección en ese \writemomento.

¿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 #1y #2similares, 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 \PushPostHookvía \show\PushPostHookproduce:

> \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@postmathy \eh@postdisplay.

Para agregar tokens a dicha macro, \PushPostHookhaga que la macro en cuestión sea igual \eh@tempiy luego redefina la macro en cuestión agregando los tokens en cuestión detrás deexpansiónde \eh@tempi.

La expansión \eh@tempies un punto crucial:

En caso de que la definición \eh@tempicontenga hashes ( #), durante la expansión, dos hashes consecutivos colapsarán en uno de ellos.

Esto implica:

Siempre que \PushPostHookse solicite agregar cosas a \eh@postmatho \eh@postdisplay, la cantidad de hashes consecutivos con cosas que ya están \eh@postmatho \eh@postdisplayque estarán dentro se reducirá a la mitad.

Esto será un problema, especialmente cuando llame \PushPostHookvarias veces para agregar cosas a \eh@postmatho \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 \these 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, \mymacrola cantidad de hashes provenientes \myscratchtoksse duplicará. Al expandirse \mymacro, esa cantidad duplicada se reducirá a la mitad y, por lo tanto, la expansión de \mymacroentrega la misma cantidad de hashes que entregaría \the\myscratchtoks.

Por lo tanto, sugiero agregar cosas a un registro de token y usarlas \PushPostHooksolo 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 \longo \global.

En el siguiente ejemplo utilicé #{la notación -para implementar una macro \mathcommandque 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)
}%

, \mathcommandel 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).
\mathcommandagregará 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 \mathcommandfromnameque 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}

ingrese la descripción de la imagen aquí

información relacionada