Macro apenas a ser definida no modo matemático

Macro apenas a ser definida no modo matemático

Costumo usar macros curtas em fórmulas matemáticas para símbolos usados ​​com frequência, por exemplo, \d xpara um dx diferencial ou, digamos, \vse 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 \drepresenta um ponto sob o próximo caractere e vo 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 \everymathesse 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:

insira a descrição da imagem aqui

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}

insira a descrição da imagem aqui

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@textse for encontrado em um arquivo \write. As proteções acima fazem com que os \mathdefcomandos se escrevam sozinhos, portanto nenhuma escolha é feita na \writehora.

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 #1e #2e 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 \PushPostHookvia \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@postmathe \eh@postdisplay.

Para adicionar tokens a tal macro, \PushPostHookpermite que a macro em questão seja igual \eh@tempie 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@tempicontenha hashes ( #), durante a expansão dois hashes consecutivos serão colapsados ​​em um deles.

Isso implica:

Sempre que \PushPostHookfor necessário adicionar coisas a \eh@postmathou \eh@postdisplay, a quantidade de hashes consecutivos com coisas que já estão \eh@postmathou \eh@postdisplayserão reduzidas pela metade.

Isso será um problema, especialmente ao ligar \PushPostHookvárias vezes para adicionar coisas a \eh@postmathou \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 \theocorre 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 \mymacroquantidade de hashes provenientes \myscratchtoksserá duplicada. Ao expandir \mymacro, essa quantidade duplicada será reduzida pela metade e, portanto, a expansão \mymacroentregará a mesma quantidade de hashes que seria entregue por \the\myscratchtoks.

Portanto, sugiro adicionar coisas a um registro de token e usá-lo \PushPostHookapenas 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 \longou \global.

No exemplo abaixo, usei #{-notation para implementar uma macro \mathcommandque 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)
}%

, \mathcommando 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).
\mathcommandadicionará 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 \mathcommandfromnameque 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}

insira a descrição da imagem aqui

informação relacionada