
Wie kann man ein Metamakro schreiben, das einem Befehl eine mit einem Stern versehene Version hinzufügt?
Die beabsichtigte Verwendung wäre etwa
\newcommand\foo[1]{foo is #1}
\addstarred\foo[2]{foo is #1, bar is #2}
Antwort1
Eine Methode ist bereits mit dem Paket suffix
von David Kastrup verfügbar. Natürlich ist es voller cleverer Tricks.
Sie können sagen
\usepackage{suffix}
\newcommand{\foo}[1]{foo is #1}
\WithSuffix\newcommand\foo*[2]{foo is #1, bar is #2}
und es kann aufschlussreich sein zu sehen, wie das Ziel erreicht wird.
Wenn wir \show\foo
nach der zweiten Anweisung vorgehen, finden wir
> \foo=\protected macro:
->\WSF@suffixcheck \foo .
also lernen wir, dass suffix
e-TeX erfordert (heutzutage kein Problem) und neu definiert wird, \foo
um zu bedeuten \WSF@suffixcheck\foo
. Also fügen wir hinzu \makeatletter
und versuchen es \show\WSF@suffixcheck
, wodurch wir erhalten
> \WSF@suffixcheck=macro:
#1->\begingroup \def \reserved@a {#1}\futurelet \reserved@b \WSF@suffixcheckii
so dass das Argument gespeichert wird in \reserved@a
und
\futurelet\reserved@b\WSF@suffixcheckii
ausgeführt wird. Dies ist \reserved@b
gleichbedeutend mit dem folgenden Token \WSF@suffixcheckii
. Wenn der Aufruf
\foo{foo}
dann \reserved@b
wird sein \bgroup
; wenn der Anruf ist
\foo*{foo}{bar}
dann \reserved@b
wird sein *
. Jetzt müssen wir wissen, was \WSF@suffixcheckii
bedeutet:
> \WSF@suffixcheckii=macro:
->\ifcsname \expandafter \SuffixName \reserved@a \reserved@b \endcsname
\expandafter \WSF@suffixcheckiii \else \expandafter \WSF@suffixcheckiv \fi .
OK, schauen wir uns an, was im \foo{foo}
Fall passiert: \reserved@a
wird zu erweitert \foo
, während (nicht erweiterbar) \reserved@b
ist \bgroup
, also wird TeX zuerst präsentiert mit
\ifcsname\SuffixName\foo\reserved@b\endcsname
und \SuffixName
ist definiert durch
> \SuffixName=\long macro:
#1->WSF:\string #1 \meaning .
Der nächste Schritt ist also
\ifcsname WSF:\string\foo \meaning\reserved@b\endcsname
und wir bekommen endlich
\ifcsname WSF:\foo begin-group character {\endcsname
wobei alle Zeichen den Kategoriecode 12 haben (aber Leerzeichen haben 10). In diesem \foo*{foo}{bar}
Fall würden wir erhalten
\ifcsname WSF:\foo the character *\endcsname
Der Befehl \csname WSF:\foo begin-group character {\endcsname
ist nicht definiert, daher wird der falsche Zweig verwendet, d. h.
\expandafter \WSF@suffixcheckiv \fi
das lässt einfach
\WSF@suffixcheckiv{foo}
im Eingabestrom. Jetzt \show\WSF@suffixcheckiv
gibt
> \WSF@suffixcheckiv=macro:
->\expandafter \endgroup \csname \expandafter \NoSuffixName \reserved@a \endcsname .
so dass die zuvor geöffnete Gruppe geschlossen wird, aber zuerst
\csname \expandafter \NoSuffixName \reserved@a \endcsname
gebildet wird. Denken Sie daran, dass \reserved@a
sich zu erweitert \foo
, also erhalten wir
\csname \NoSuffixName \foo \endcsname
und \NoSuffixName
ist
> \NoSuffixName=macro:
->WSF:\string .
so erhalten wir schließlich
\csname WSF:\string\foo\encsname
OK, geben wir Folgendes aus \expandafter\show\csname WSF:\string\foo\endcsname
:
> \WSF:\foo=\long macro:
#1->foo is #1.
das heißt, dieses komplizierte Makro ist eine Kopie des Originals \foo
.
Im Falle von \foo*{foo}{bar}
hätten wir
\ifcsname WSF:\foo the character *\endcsname
aber in diesem FallIstdefiniert; in der Tat
\expandafter\show\csname WSF:\string\foo\space the character *\endcsname
produziert
> \WSF:\foo the character *=\long macro:
#1#2->foo is #1, bar is #2.
also ist dieses Makro mit dem komplizierten Namen das, was Sie als -Variante definiert haben *
.
Mit diesem Paket kann fast jedes Token als Suffix verwendet werden. Aber die grundlegende Idee unterscheidet sich nicht von dem, was Sie sich ausgedacht haben; der Schutz gegen das Überschreiben möglicher vorhandener Makronamen ist besser. Was das Paket macht, wenn
\WithSuffix\newcommand\foo*[2]{foo is #1, bar is #2}
wird verarbeitet wird
Speichern Sie den Originalbefehl
\foo
unter\csname WSF:\string\foo\endcsname
(sofern dieses bereits durch ein vorangegangenes vorhanden ist, entfällt
\WithSuffix
dieser\foo
Schritt natürlich)Speichern Sie die neue Definition unter
\csname WSF:\string\foo\space the character *\endcsname
Verwenden Sie die oben beschriebene abstrakte Schnittstelle, um zwischen verschiedenen Suffixen zu wählen.
Antwort2
Unten finden Sie meinen eigenen Lösungsversuch mit Verbesserungen, die freundlicherweise von @egreg und @DavidCarlisle bereitgestellt wurden.
\documentclass{standalone}
\makeatletter
\newcommand\addstarred[1]{%
\expandafter\let\csname\string#1@nostar\endcsname#1%
\edef#1{\noexpand\@ifstar\expandafter\noexpand\csname\string#1@star\endcsname\expandafter\noexpand\csname\string#1@nostar\endcsname}%
\expandafter\newcommand\csname\string#1@star\endcsname%
}
\makeatother
\newcommand\foo[1]{foo is #1}
\addstarred\foo[2]{foo is #1, bar is #2}
\begin{document}
\foo{red} --- \foo*{red}{green}
\end{document}
Ergebnis:
Erläuterung:
- Eine Kopie der aktuellen Definition des Befehls
\foo
wird als gespeichert\\foo@nostar
. - Der Befehl
\foo
wird neu definiert, um nach einem Stern zu suchen und entweder\\foo@star
oder aufzurufen\\foo@nostar
. Dies geschieht mitedef
, damit die erstellten Token-Namen direkt erweitert werden können und nicht bei jedem Aufruf des Befehls. - Ein
\newcommand
for\\foo@star
wird gestartet und übernimmt den Rest der Definition wie folgt\addstarred\foo
.