
Ich verwende in mathematischen Formeln oft kurze Makros für häufig verwendete Symbole, z. B. \d x
fü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 \d
für einen Punkt unter dem nächsten Zeichen und v
fü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 \everymath
diese 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:
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}
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 \mathdef
Befehle selbst schreiben, sodass zu diesem \write
Zeitpunkt 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 \PushPostHook
Via \show\PushPostHook
ergibt:
> \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@postmath
und \eh@postdisplay
.
Um Token zu einem solchen Makro hinzuzufügen, \PushPostHook
lässt man das betreffende Makro gleich \eh@tempi
und definiert das betreffende Makro dann neu, indem man die betreffenden Token hinter demErweiterungvon \eh@tempi
.
Der Ausbau \eh@tempi
ist ein entscheidender Punkt:
Falls die Definition \eh@tempi
Hashes () enthält #
, werden bei der Erweiterung zwei aufeinanderfolgende Hashes zu einem einzigen zusammengefasst.
Dies bedeutet:
Immer wenn \PushPostHook
zum Hinzufügen von Dingen zu \eh@postmath
oder aufgerufen wird \eh@postdisplay
, wird die Anzahl der aufeinanderfolgenden Hashes mit Dingen, die bereits in \eh@postmath
oder enthalten sind \eh@postdisplay
, halbiert.
Dies ist insbesondere dann ein Problem, wenn zum Hinzufügen von Dingen zu oder \PushPostHook
mehrere 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 \the
wä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 \mymacro
die Menge der von stammenden Hashes \myscratchtoks
verdoppelt. Bei der Erweiterung von \mymacro
wird diese verdoppelte Menge halbiert und somit \mymacro
liefert 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 \PushPostHook
nur 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 \long
oder 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 , \mathcommand
ist 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)
.
\mathcommand
fü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}