
Eng verwandt:Wie implementiert man \expandbefore ähnlich wie \expandafter?
In der zugehörigen Frage wird auf verwiesen "# Notation"
. Wie funktioniert die Syntax?
Aus einer Antwort auf die andere Frage:
\makeatletter
%
\newcommand\name{}%
\long\def\name#1#{\UD@innername{#1}}%
%
\newcommand\UD@innername[2]{%
\expandafter\UD@exchange\expandafter{\csname#2\endcsname}{#1}%
}%
%
\newcommand\UD@exchange[2]{#2#1}%
%
\makeatother
Wie funktioniert die Parametersyntax (ohne Zahl) eigentlich? \def\test#1#{\csname test#1\endcsname}? Was sind gute Anwendungsfälle für dieses Muster?
Antwort1
Der Kerngedanke \name
ist:
Mit
\name⟨stuff without braces before the left-brace⟩{⟨stuff within braces⟩}
TeX soll als erstes Argument fetch ⟨stuff without braces before the left-brace⟩
und als zweites Argument process angeben ⟨stuff within braces⟩
:
Dies ⟨stuff within braces⟩
wird als Name eines Steuersequenz-Tokens verwendet, der durch Einschließen ⟨stuff within braces⟩
in \csname..\endcsname
und Ausführen des resultierenden \csname..\endcsname
-Ausdrucks erhalten werden kann.
Stattdessen steht dahinter ein Steuersequenz-Token .{⟨stuff within braces⟩}
⟨stuff without braces before the left-brace⟩
Wenn Makros mit abgegrenzten Argumenten ausgeführt werden, sammelt (La)TeX die Token, die die Argumente bilden, aus dem Token-Stream und sucht dabei im Token-Stream nach den Argument-Begrenzern, da die Argument-Begrenzer als Markierungen zum Beenden des Prozesses des Sammelns von Token für das betreffende Argument verwendet werden.
Nach welchen Trennzeichen gesucht werden soll, ist in der⟨Parametertext⟩der Definition.
Man kann angeben, dass (La)TeX als Trennzeichen für das letzte Argument suchen soll, z.B. a, \relax
indem man \relax
als letztes Element des⟨Parametertext⟩.
Sie können angeben, dass (La)TeX als Trennzeichen für das letzte Argument eine linke Klammer verwenden soll, indem Sie #
als letztes Element des⟨Parametertext⟩.
In (fast ;-) ) jedem Fall die⟨Parametertext⟩einer Makrodefinition folgt die⟨Ersatztext⟩, in Klammern verschachtelt.
Somit ist in (fast ;-) ) jedem Fall der⟨Parametertext⟩einer Makrodefinition folgt die linke Klammer des Klammernpaars, das die⟨Ersatztext⟩Wenn also
das letzte Token des⟨Parametertext⟩ist ein #
, wird es von einem gefolgt {
.
Deshalb wird dieses Ding, das angibt, dass (La)TeX beim Erfassen des letzten Arguments für das Makro nach einer linken Klammer als Trennzeichen suchen soll, auch #{
-Notation genannt.
Der feine Unterschied zwischen der Suche nach \relax
und der Suche nach einer öffnenden Klammer ist:
Wenn (La)TeX beispielsweise als Trennzeichen für ein Argument eines Makros sucht \relax
und es findet, beendet es nicht nur das Sammeln von Token für das Argument, sondern entfernt auch das Trennzeichen/das \relax
-Token.
Wenn (La)TeX als Trennzeichen für das letzte Argument eines Makros die linke Klammer sucht und findet, hört es auf, Token für das letzte Argument zu sammeln, lässt das Trennzeichen/die Klammer an Ort und Stelle und setzt das Makro⟨Ersatztext⟩bevor.
Zitate aus Prof. Donald Ervin Knuths TeXbook, Kapitel 20: Definitionen (auch Makros genannt)— Prof. Donald Ervin Knuth,emeritierter Professor für Computerprogrammierung an der Stanford University, ist der Erfinder von TeX:
Nachdem wir nun eine Reihe von Beispielen gesehen haben, schauen wir uns die genauen Regeln an, die für TeX-Makros gelten. Definitionen haben die allgemeine Form
\def⟨Steuersequenz⟩⟨Parametertext⟩{⟨Ersatztext⟩}
bei dem die⟨Parametertext⟩keine Klammern enthält und alle Vorkommen von{Und }im⟨Ersatztext⟩sind richtig verschachtelt. Außerdem hat das #-Symbol eine besondere Bedeutung: In der ⟨Parametertext⟩muss auf das erste # eine 1 folgen, auf das nächste eine 2 usw.; bis zu neun # sind erlaubt. Im⟨Ersatztext⟩ Auf jedes # muss eine Ziffer folgen, die nach # im⟨Parametertext⟩, oder auf das # muss ein weiteres # folgen. Letzterer Fall steht für ein einzelnes #-Token, wenn das Makro erweitert wird; ersterer Fall steht für das Einfügen des entsprechenden Arguments.
[...]
Eine spezielle Erweiterung dieser Regeln ist zulässig: Wenn das allerletzte Zeichen der⟨Parametertext⟩ist #, so dass auf dieses # unmittelbar folgt {, TeX verhält sich so, als ob die{wurde am rechten Ende des Parametertextes und des Ersetzungstextes eingefügt. Wenn Sie beispielsweise sagen
'\def\a#1#{\hbox zu #1}'
, der nachfolgende Text '\a3pt{x}' wird zu '\hbox to 3pt{x}' erweitert, da das Argument von \a durch eine linke Klammer begrenzt ist.
Mit anderen Worten:
Wenn Sie sagen , wird der nachfolgende Text wie folgt verarbeitet:\def\a#1#{\hbox to #1}
\a3pt{x}
Als Argument für das Makro \a
wird TeX aus dem nachfolgenden Text ein Argument entnehmen, das durch eine linke Klammer begrenzt ist: Es wird die Phrase entnommen 3pt
.
Die linke Klammer (und alles dahinter) wird beim Ersetzen \a
durch das Argument an Ort und Stelle belassen.⟨Ersatztext⟩ergibt:, \hbox to 3pt
so dass das Ganze zu folgendem wird: \hbox to 3pt{x}
.
Kurz gesagt: #{
-Notation (beachten Sie die linke Klammer!) bedeutet, dass der letzte Parameter des betreffenden Makros durch eine linke Klammer (mit dem Kategoriecode 1 (Beginn der Gruppe)) abgegrenzt wird, die an Ort und Stelle bleibt, wenn (La)TeX das letzte Argument für dieses Makro aus dem Token-Stream sammelt.
Die Tatsache, dass in diesem Sonderfall das Trennzeichen, also die linke Klammer, an Ort und Stelle bleibt, ist bemerkenswert, da dies die einzige Situation ist, in der ein Argumenttrennzeichen während des Prozesses zum Erfassen eines Arguments nicht aus dem Token-Stream entfernt wird.
D.h. wenn das Argumenttrennzeichen beispielsweise ein \relax
-Token wäre wie in
\def\mymacro#1\relax{The Argument#1 was delimited by relax. }
\relax
würde das als Trennzeichen dienende -Token beim Sammeln des Arguments aus dem Token-Stream entfernt: Mit
\mymacro , which is nonsense,\relax...
als Argument für \mymacro
(La)TeX würde aus dem Token-Stream die Sequenz sammeln , which is nonsense,
und dann das Token finden \relax
und dieses Token als Trennzeichen für das Argument verwenden \relax
, und daher würde (La)TeX aufhören, Token für das Argument zu sammeln und das Trennzeichen entfernen. (Dann würde es beginnen, die anderen Argumente von \mymacro
if gemäß dem⟨Parametertext⟩seiner Definition gab es welche. Aber es gibt keine.) Dann würde es die⟨Ersatztext⟩:
The Argument, which is nonsense, was delimited by relax.
Das⟨Ersatztext⟩würde im Token-Stream von den drei Punkten gefolgt werden und daher würde der Token-Stream jetzt enthalten:
The Argument, which is nonsense, was delimited by relax. ...
Aber wenn die Definition lautet:
\def\mymacro#1#{The Argument#1 was delimited by a left-brace. }
und du sagst
\mymacro , which is nonsense,{...
, Sie erhalten
The Argument, which is nonsense, was delimited by a left-brace. {...
weil im Gegensatz zum vorherigen Fall, wo die \relax
das Argument begrenzende Klammer entfernt wird, in diesem Fall die das Argument begrenzende linke Klammer nicht entfernt wird.
Der Satz
Wenn das allerletzte Zeichen der⟨Parametertext⟩ist #, so dass auf dieses # unmittelbar folgt {, TeX verhält sich so, als ob die{wurde sowohl am rechten Ende des Parametertextes als auch des Ersetzungstextes eingefügt.
bedeutet:
Eine Definition ist normalerweise ein Muster
\def⟨Steuersequenz⟩⟨Parametertext⟩{⟨Ersatztext⟩}
Angenommen, Sie möchten ein Makro definieren \macro
, das zwei Argumente verarbeitet, von denen das erste Argument nicht begrenzt ist und das zweite Argument durch die Sequenz begrenzt ist, \foo\bar
und das die Argumente einfach „ausspuckt“.
In diesem Fall erstellen Sie einen Ausdruck wie:
\def\macro#1#2\foo\bar{argument 1: #1 argument 2: #2}
⟨Steuersequenz⟩=\macro
⟨Parametertext⟩=#1#2\foo\bar
⟨Ersatztext⟩=argument 1: #1 argument 2: #2
Wenn Sie den Ausdruck betrachten, sehen Sie, dassam rechten Ende des⟨Parametertext⟩, es werden die Tokens angezeigt, die das Trennzeichen des letzten Arguments bilden, also die Token \foo\bar
. Sie gehören zu den Token, die die⟨Parametertext⟩. Direkt dahinter sehen Sie die linke Klammer des Klammernpaars, das die⟨Ersatztext⟩.
In diesem Fall also am rechten Ende des⟨Parametertext⟩Sie finden die Token, die das letzte Argument begrenzen, und Sie finden die linke Klammer des Klammernpaars, das das⟨Ersatztext⟩.
Der Ablauf
\macro{A}B\foo\bar
ergibt:
argument 1: A argument 2: B
\foo\bar
Wie Sie sehen, wurden beim Sammeln der zu den Argumenten gehörenden Token die Trenntoken entfernt.
Wenn Sie stattdessen definieren:
\def\macro#1#2\foo\bar{argument 1: #1 argument 2: #2\foo\bar}
⟨Steuersequenz⟩=\macro
⟨Parametertext⟩=#1#2\foo\bar
⟨Ersatztext⟩=argument 1: #1 argument 2: #2\foo\bar
,d. h., wenn Sie das Trennzeichen \foo\bar
am rechten Ende des⟨Ersatztext⟩Auch, der Ablauf
\macro{A}B\foo\bar
Erträge
argument 1: A argument 2: B\foo\bar
als ob bei der vorherigen Definition das Trennzeichen an Ort und Stelle belassen worden wäre.
Was wäre, wenn Sie so etwas definieren möchten, das Trennzeichen jedoch keine Sequenz, \foo\bar
sondern eine linke Klammer wäre?
Die Definition sollte immer noch ein Muster sein
\def\macro#1#2⟨delimiter⟩{argument 1: #1 argument 2: #2⟨delimiter⟩}
aber aus mehreren Gründen kann man nicht {
als⟨Trennzeichen⟩und schreibe
\def\macro#1#2{{argument 1: #1 argument 2: #2{}
und nehme hiermit #1#2{
für die⟨Parametertext⟩und argument 1: #1 argument 2: #2{
für die⟨Ersatztext⟩:
In vielen Situationen können einzelne linke Klammern nicht einfach eingefügt werden, ohne dass aufgrund ungleichmäßiger Klammern Fehler auftreten.
Die Situation wäre mehrdeutig, weil (La)TeX raten müsste, ob eine öffnende Klammer am rechten Ende des⟨Parametertext⟩ist ein Argumenttrennzeichen oder gehört es zu dem Klammernpaar, das das⟨Ersatztext⟩.
(La)TeX müsste auch erraten, ob die rechte Klammer einfach mit der linken Klammer am Ende des⟨Ersatztext⟩oder ob es das Ende der⟨Ersatztext⟩.
Daher wurde als syntaktischer Workaround #{
die -Notation erfunden:
Mit
\def\macro#1#2#{argument 1: #1 argument 2: #2}
⟨Steuersequenz⟩= \macro
.
⟨Parametertext⟩=#1#2#
⟨Ersatztext⟩=argument 1: #1 argument 2: #2
, Die⟨Parametertext⟩endet nicht mit einem Token {
, das das letzte Argument abgrenzen soll, aber es endet mit #
.
Damit #
wird angegeben, dass (La)TeX beim Expandieren \macro
der Token des letzten Arguments \macro
aus dem Token-Stream nach einer linken Klammer als Argument-Trennzeichen suchen soll. Dies #
bedeutet auch, dass die als Trennzeichen gefundene linke Klammer an Ort und Stelle bleiben soll, damit die⟨Ersatztext⟩von \macro
geht davor.
Mit anderen Worten:
Das #
führt dazu, dass sich (La)TeX so verhält, als ob als letztes Token, das das letzte Argument abgrenzt, eine linke Klammer gefunden wurde.⟨Parametertext⟩.
Es bewirkt auch, dass (La)TeX sich so verhält, als ob das letzte Token in der Definition⟨Ersatztext⟩war eine linke Klammer.
Zu Ihrem Kommentar:
Ähm. Ich nehme an, Sie können sich nichts ausdenken? Eine Vermutung darüber, was vor sich geht? Lassen Sie es mich anders formulieren: In Ihrem anderen Beispiel haben Sie überhaupt Zeichen des Cat-Codes 1, die in all dem herumschwirren. Ist es richtig, daraus zu schließen, dass dieses „primitive Verhalten“ während der Eingabephase beim Lesen der Datei auftritt? (Entschuldigung, ich erfinde die Terminologie, während ich schreibe ... ich spreche vom
process_input_buffer
Ereignishandler.
Die allererste Phase der Verarbeitung in LaTeX ist das Lesen der Eingabe (aus der Datei oder von der Konsole) und das Übernehmen dieser Eingabe in eine Reihe von Anweisungen zum EinfügenTokenin den Token-Stream. (Zeichen-Token/Steuersequenz-Token). In nachfolgenden Phasen werden die Token im Token-Stream verarbeitet. In einer früheren dieser Phasen werden erweiterbare Token, z. B. Makros, erweitert. D. h., sie werden durch jene Token ersetzt, die die Ersetzungstexte ihrer Definitionen bilden. In dieser Phase findet das Sammeln von Token für Makro-Argumente statt. Das Verhalten findet also nicht beim Lesen der Datei statt, sondern nach dem Lesen und Tokenisieren, in der Phase des Erweiterns erweiterbarer Token und dem damit verbundenen Sammeln andererTokenals ihre Argumente.
Ich habe versucht, in meiner Antwort auf die Frage ausführlich zu erläutern, wie (La)TeX Makroargumente sammelt und verarbeitetWie sucht TeX nach abgegrenzten Argumenten?
\expandafter
Ich habe versucht , in meiner Antwort auf die Frage ausführlich auf Möglichkeiten zur Vermeidung dieser Gefahr einzugehen.Wie kann ich die Anzahl der Expandafters beim Anhängen an ein CSNAME-Makro ermitteln?
\name
Ich habe versucht , in meiner Antwort auf die Frage ausführlich auf das Makro einzugehenDefinieren Sie eine Steuersequenz, danach kommt ein Leerzeichen