Makro zum Hinzufügen einer mit Sternchen markierten Befehlsversion

Makro zum Hinzufügen einer mit Sternchen markierten Befehlsversion

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 suffixvon 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\foonach der zweiten Anweisung vorgehen, finden wir

> \foo=\protected macro:
->\WSF@suffixcheck \foo .

also lernen wir, dass suffixe-TeX erfordert (heutzutage kein Problem) und neu definiert wird, \fooum zu bedeuten \WSF@suffixcheck\foo. Also fügen wir hinzu \makeatletterund 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@aund

\futurelet\reserved@b\WSF@suffixcheckii

ausgeführt wird. Dies ist \reserved@bgleichbedeutend mit dem folgenden Token \WSF@suffixcheckii. Wenn der Aufruf

\foo{foo}

dann \reserved@bwird sein \bgroup; wenn der Anruf ist

\foo*{foo}{bar}

dann \reserved@bwird sein *. Jetzt müssen wir wissen, was \WSF@suffixcheckiibedeutet:

> \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@awird zu erweitert \foo, während (nicht erweiterbar) \reserved@bist \bgroup, also wird TeX zuerst präsentiert mit

\ifcsname\SuffixName\foo\reserved@b\endcsname

und \SuffixNameist 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 {\endcsnameist 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@suffixcheckivgibt

> \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@asich zu erweitert \foo, also erhalten wir

\csname \NoSuffixName \foo \endcsname

und \NoSuffixNameist

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

  1. Speichern Sie den Originalbefehl \foounter

    \csname WSF:\string\foo\endcsname
    

    (sofern dieses bereits durch ein vorangegangenes vorhanden ist, entfällt \WithSuffixdieser \fooSchritt natürlich)

  2. Speichern Sie die neue Definition unter

    \csname WSF:\string\foo\space the character *\endcsname
    
  3. 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:

MWE-Ausgabe

Erläuterung:

  • Eine Kopie der aktuellen Definition des Befehls \foowird als gespeichert \\foo@nostar.
  • Der Befehl \foowird neu definiert, um nach einem Stern zu suchen und entweder \\foo@staroder aufzurufen \\foo@nostar. Dies geschieht mit edef, damit die erstellten Token-Namen direkt erweitert werden können und nicht bei jedem Aufruf des Befehls.
  • Ein \newcommandfor \\foo@starwird gestartet und übernimmt den Rest der Definition wie folgt \addstarred\foo.

verwandte Informationen