Makro nur im Mathematikmodus zu definieren

Makro nur im Mathematikmodus zu definieren

Ich verwende in mathematischen Formeln oft kurze Makros für häufig verwendete Symbole, z. B. \d xfür ein Differential dx oder\v wenn ich beispielsweise einen Vektor benötigegegensehr oft in meinem Text, um die Lesbarkeit zu verbessern. Allerdings kollidiere ich mit vordefinierten Makros, weil sie \dfür einen Punkt unter dem nächsten Zeichen und vfür den Hacek-Akzent stehen, den ich nicht überschreiben möchte (vielleicht brauche ich sie in der Bibliographie ...).

Daher habe ich mir Folgendes ausgedacht, um diese Makros nur im Mathematikmodus zu überschreiben:

\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}

Ich möchte jedoch, dass der Befehl auch wie normale Makros definiert wird, also \mathdef{\d}{\mathrm{d}}, und auch Argumente annehmen kann, aber alle meine Experimente mit expandafter, unexpanded, usw. haben nicht funktioniert und nur zu den üblichen seltsamen Fehlermeldungen geführt. Irgendein Hinweis, wie ich das machen kann?

Glauben Sie außerdem, dass die Leistung erheblich beeinträchtigt wird, wenn man \everymathdiese Methode in einem großen Dokument verwendet?

Antwort1

Meine Idee ist der von egreg sehr ähnlich, aber ich möchte ein optionales Argument hinzufügen, damit der mathematische Befehl Argumente selbst verarbeiten kann. Der Code:

\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}

Das Ergebnis:

Bildbeschreibung hier eingeben

Antwort2

Ich würde es nicht verwenden \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}

Bildbeschreibung hier eingeben

Ich glaube allerdings nicht, dass das ein guter Weg ist. Es ist verwirrend und fehleranfällig.

SehenWann soll \LetLtxMacro verwendet werden?für Informationen über \LetLtxMacro.

Etwas einfacher geht es mit 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}

Warum verwende ich überall \DeclareRobustCommand(erste Version) oder (zweite Version)?\protected

Wenn TeX eine Schreiboperation durchführt, befindet es sich in keinem Modus, insbesondere nicht im Mathematikmodus. Eine vereinfachte Definition wie

\newcommand{\foo}{%
  \relax % if this comes first in an alignment
  \ifmmode
    \expandafter\@firstoftwo
  \else
    \expandafter\@secondoftwo
  \fi
  \foo@math\foo@text
}

würdestetsWählen Sie \foo@text, wenn in einem gefunden \write. Die oben genannten Schutzmaßnahmen sorgen dafür, dass sich die \mathdefBefehle selbst schreiben, sodass zu diesem \writeZeitpunkt keine Auswahl getroffen wird.

Warum nicht verwenden \everymath? Probieren Sie das folgende Beispiel aus und überzeugen Sie sich selbst.

\documentclass{article}

\everymath{\let\foo\foomath}
\newcommand{\foo}{Foo}
\newcommand{\foomath}{Ops}

\begin{document}

Text: \foo

Math: $\foo$

Ops: abc\textsuperscript{\foo}

\end{document}

Antwort3

Sie möchten das \PushPostHook-Makro aus demjederhaken-Paket zum (Neu-)Definieren von Makros, die Argumente verarbeiten?

Fehler 1:

In LaTeX werden Argumente durch #1„und #2“ und dergleichen gekennzeichnet, also durch Hash-Folgen, gefolgt von einer Ziffer im Bereich 1..9.

Beim Verschachteln von Definitionen müssen die Hashes für die inneren Definitionen verdoppelt werden:

\def\outsidemacro#1{%
  \def\insidemacro##1{This is insidemacro's argument: ##1.}%  
  This is outsidemacro's argument: #1.%
}

Bei der Expansion werden jeweils zwei aufeinanderfolgende Hashes zu einem Hash zusammengefasst, das heißt, die Anzahl der Hashes wird halbiert.

D. h., es \outsidemacro{foo}ergibt sich:

\def\insidemacro#1{This is insidemacro's argument: #1.}%  
This is outsidemacro's argument: foo.%

Ein Blick auf die Definition von \PushPostHookVia \show\PushPostHookergibt:

> \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 .

Wie Sie sehen, werden die Token in Makros gespeichert, deren Namen dem Muster entsprechen .\eh@post⟨name of hook⟩

Z. B. \eh@postmathund \eh@postdisplay.

Um Token zu einem solchen Makro hinzuzufügen, \PushPostHooklässt man das betreffende Makro gleich \eh@tempiund definiert das betreffende Makro dann neu, indem man die betreffenden Token hinter demErweiterungvon \eh@tempi.

Der Ausbau \eh@tempiist ein entscheidender Punkt:

Falls die Definition \eh@tempiHashes () enthält #, werden bei der Erweiterung zwei aufeinanderfolgende Hashes zu einem einzigen zusammengefasst.

Dies bedeutet:

Immer wenn \PushPostHookzum Hinzufügen von Dingen zu \eh@postmathoder aufgerufen wird \eh@postdisplay, wird die Anzahl der aufeinanderfolgenden Hashes mit Dingen, die bereits in \eh@postmathoder enthalten sind \eh@postdisplay, halbiert.

Dies ist insbesondere dann ein Problem, wenn zum Hinzufügen von Dingen zu oder \PushPostHookmehrere Aufrufe ausgeführt werden .\eh@postmath\eh@postdisplay

Sie können die Halbierung der Anzahl aufeinanderfolgender Hashes vermeiden, indem Sie die Dinge mithilfe eines Token-Registers verwalten, denn wenn der Inhalt eines Token-Registers über "ausgespuckt" wird \the, wird die Anzahl der Hashes nicht reduziert. Wenn das "Ausspucken" aufgrund von \thewährend stattfindet \edef, wird die Anzahl der Hashes nicht nur nicht reduziert, sondern verdoppelt.

Wenn Sie beispielsweise

\myscratchtoks{#}
\edef\mymacro{\the\myscratchtoks}

, in der Definition wird \mymacrodie Menge der von stammenden Hashes \myscratchtoksverdoppelt. Bei der Erweiterung von \mymacrowird diese verdoppelte Menge halbiert und somit \mymacroliefert die Erweiterung von die gleiche Menge an Hashes, die von geliefert würde \the\myscratchtoks.

Daher schlage ich vor, Dinge zu einem Token-Register hinzuzufügen und es \PushPostHooknur zum „Leeren“ dieses Token-Registers zu verwenden.

Problem 2:

Wenn Sie Makros verwalten möchten, die auch Argumente verarbeiten können, schlage ich vor, etwas zu implementieren, das ähnlich wie die Präfixe \longoder verwendet werden kann \global.

Im folgenden Beispiel habe ich #{die -Notation verwendet, um ein Makro zu implementieren \mathcommand, das durch linke Klammern begrenzte Argumente verarbeitet, denen in Klammern verschachtelte Argumente folgen. Da die ⟨definition text⟩eines Makros immer in Klammern verschachtelt werden sollen, können Sie die Verarbeitung von durch linke Klammern begrenzten Argumenten verwenden, um alle Token abzurufen, die vor einem stehen ⟨definition text⟩.
Solche Token können der Definitionsbefehl selbst (z. B. \renewcommand*oder \global\long\def), das (neu) zu definierende Kontrollsequenz-Token und das sein ⟨parameter text⟩.

Beispielsweise mit

\mathcommand\renewcommand*\mymacrowitharguments[2]{%
  \mbox{math-arg~1: }(#1) 
  \mbox{ math-arg~2: }(#2)
}%

Das erste (durch eine linke Klammer getrennte) Argument von , \mathcommandist die Sequenz \renewcommand*\mymacrowitharguments[2]und sein zweites Argument wird durch das in Klammern verschachtelte Material gebildet: \mbox{math-arg~1: }(#1) \mbox{ math-arg~2: }(#2).
\mathcommandfügt die Sequenz , d. h. die Sequenz, zum Token-Register hinzu .⟨first argument⟩{⟨second argument⟩}\renewcommand*\mymacrowitharguments[2]{\mbox{math-arg~1: }(#1) \mbox{ math-arg~2: }(#2) }\mymathhooktoks

Ich habe auch ein Makro implementiert \mathcommandfromname, das auch ein durch eine linke Klammer getrenntes Argument abruft und das Material nimmt, das sich hinter diesem Argument befindet und daher in Klammern verschachtelt ist, für den Namen eines Steuersequenz-Tokens, das wie folgt gebildet werden kann \csname..\endcsname:

Z.B,

\mathcommandfromname\renewcommand*{mymacrowitharguments}[2]{%
  \mbox{math-arg~1: }(#1) 
  \mbox{ math-arg~2: }(#2)
}%

ergibt:

\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}

Bildbeschreibung hier eingeben

verwandte Informationen