%20definiert%20werden%20kann%3F.png)
Gibt es ein Token, das nicht durch \uppercase
/ beeinflusst \lowercase
und in Bezug auf nicht (neu) definierbar ist \outer
?
(Wenn ja, würde ich es gerne als Argumenttrennzeichen für Dinge verwenden, die innerhalb von \uppercase
/ verwendet werden können \lowercase
.)
Antwort1
Soweit ich weiß, gibt es kein solches Token:
Jeder Token gehört zu mindestens einer der folgenden beiden Token-Klassen – aktive Charakter-Token gehören zu beiden Klassen gleichzeitig:
- Steuersequenzen. (Steuerwort-Token, Steuersymbol-Token, aktive Zeichen-Token.) Alle Steuersequenzen sind hinsichtlich (neu)definierbar
\outer
. - Explizite Zeichentoken. Jedes explizite Zeichentoken kann durch
\uppercase
/ beeinflusst werden\lowercase
(vorausgesetzt, sein\uccode
/\lccode
ist entsprechend gesetzt).
In vielen Situationen können Sie durch Trennzeichen gekennzeichnete Argumente vollständig vermeiden, indem Sie stattdessen mit durch Trennzeichen gekennzeichneten Argumenten arbeiten und eine Prüfung auf Leerheit durchführen.
Um beispielsweise das erste unbegrenzte Argument aus einer durch Klammern ausgeglichenen Token-Liste zu extrahieren, verwende ich oft so etwas wie das hier:
%% \romannumeral\UD@ExtractFirstArgLoop{<argument>\UD@SelDOm}%
%% yields <argument>'s 1st undlimited argument.
%% <argument> must not be blank, i.e., must neither be empty nor consist
%% only of explicit character tokens of catcode 10 and charcode 32.
%%
%% \UD@SelDOm must not be defined in terms of \outer !
%%.............................................................................
\@ifdefinable\UD@RemoveTillUD@SelDOm{%
\long\def\UD@RemoveTillUD@SelDOm#1#2\UD@SelDOm{{#1}}%
}%
\newcommand\UD@ExtractFirstArgLoop[1]{%
\expandafter\UD@CheckWhetherNull\expandafter{\@firstoftwo{}#1}%
{\expandafter\z@\@secondoftwo{}#1}%
{\expandafter\UD@ExtractFirstArgLoop\expandafter{\UD@RemoveTillUD@SelDOm#1}}%
}%
( \UD@CheckWhetherNull
definiert als
%%-----------------------------------------------------------------------------
%% Check whether argument is empty:
%%.............................................................................
%% \UD@CheckWhetherNull{<Argument which is to be checked>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked is empty>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked is not empty>}%
\newcommand\UD@CheckWhetherNull[1]{%
\romannumeral\expandafter\@secondoftwo\string{\expandafter
\@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
\@secondoftwo\string}\expandafter\@firstoftwo\expandafter{\expandafter
\@secondoftwo\string}\expandafter\z@\@secondoftwo}%
{\expandafter\z@\@firstoftwo}%
}%
oder als
\newcommand\UD@CheckWhetherNull[1]{%
\romanumeral\ifcat$\detokenize{#1}$%
\expandafter\expandafter\expandafter\z@\expandafter\@firstoftwo\else
\expandafter\expandafter\expandafter\z@\expandafter\@secondoftwo\fi
}%
. )
Damit erhalten Sie:
\romannumeral\UD@ExtractFirstArgLoop{{A}{B}CDE\UD@SelDOm}%
→ A
.
Um die Anzahl der Iterationen zu verringern, die zum Entfernen von allem außer dem ersten nicht abgegrenzten Argument erforderlich sind, habe ich ein durch abgegrenztes Argument verwendet \UD@SelDOm
. (Die Anzahl der erforderlichen Iterationen ist: „Anzahl der \UD@SelDOm
nicht in Klammern verschachtelten Argumente innerhalb des Arguments“ + 1).
\UD@SelDOm
Wenn Ihnen das -Trennzeichen, wie es möglicherweise in Bezug auf definiert ist , nicht gefällt \outer
, können Sie wie folgt darauf verzichten – allerdings sind dann mehr Iterationen erforderlich, bis das Ergebnis vorliegt. Die Anzahl der erforderlichen Iterationen lautet: „Anzahl der nicht getrennten Argumente innerhalb des Arguments, die selbst nicht in Klammern verschachtelt sind“ + 1:
% Syntax now is: \romannumeral\UD@ExtractFirstArgLoop{<argument>{}}%
\newcommand\UD@GrabFirst[2]{{#1}}%
\renewcommand\UD@ExtractFirstArgLoop[1]{%
\expandafter\UD@CheckWhetherNull\expandafter{\@firstoftwo{}#1}%
{\expandafter\z@\@secondoftwo{}#1}%
{\expandafter\UD@ExtractFirstArgLoop\expandafter{\UD@GrabFirst#1}}%
}%
Z.B,
\romannumeral\UD@ExtractFirstArgLoop{{A}{BC}DE{}}%
→A
Es gibt viele Situationen, in denen Sie ähnliche Dinge tun können, um abgegrenzte Argumente vollständig zu vermeiden.
Manchmal wird als Argumenttrennzeichen das Makro-Token verwendet, das das betreffende Argument verarbeitet, z. B. \def\macro#1\macro{...}
. Ob dies möglich/sinnvoll ist, hängt von der Frage ab, ob es \macro
in seinem eigenen Argument verschachtelt sein und dadurch das Trennzeichen fälschlicherweise zuordnen könnte oder ob so etwas \let\macrob=\macro \outer\def\macro...
passieren könnte.
Da Sie bereits über die Frage nachdenken, wie Makroargumente sicher gemacht werden können, möchte ich darauf hinweisen, dass es neben \outer\def...
und \uppercase
/ noch weitere Fallen gibt \lowercase
:
Beispielsweise die Frage, ob ein auf abgegrenzten Argumenten basierender Makromechanismus, der (fast) beliebige vom Benutzer angegebene Argumente verarbeiten soll, in einer tabellarischen Umgebung/innerhalb einer Ausrichtung funktionieren sollte.
Nehmen wir beispielsweise ein Makro an,
\grabdelimited
das ein abgegrenztes Argument verarbeitet und das der Benutzer zum Sammeln und Detokenisieren der ZeichenB
und verwendet&
.\documentclass{article} \def\grabdelimited#1\delimiter{Arguments grabbed: \detokenize{#1}}% \begin{document} \grabdelimited B&\delimiter \makeatletter \begin{tabular}{|l|l|} %A&\relax\grabdelimited B&\delimiter\\ A&\relax\expandafter\@firstofone\expandafter{\grabdelimited B&\delimiter} \end{tabular} \end{document}
Die erste/kommentierte Zeile innerhalb der tabellarischen Umgebung würde einen Fehler ergeben, die zweite nicht, da hier die
&
Zugehörigkeit zum abgegrenzten Argument innerhalb der geschweiften Klammern von verborgen ist\@firstofone
.Ein weiteres Problem könnte die Übergabe unausgeglichener
\if...
/\else
/\fi
als Makroargumente sein, die fälschlicherweise mit einigen\if...
/ übereinstimmen\else
, die in den Definitionen der Makros vorkommen, die diese Argumente verarbeiten.Dasselbe gilt für unausgeglichenes
\csname
/\endcsname
.
Antwort2
Erweiterter Kommentar:
Wenn Sie wirklich Angst haben, dass Ihr Argumentende-Marker von \uppercase
/ beeinflusst werden könnte \lowercase
, müssen Sie nur sicherstellen, dass er nicht auf der obersten Ebene erscheint, sodass er nie von \uppercase
oder gesehen wird \lowercase
. Dies könnte erreicht werden, indem sichergestellt wird, dass er nur in einem Kontext eingefügt wird, in dem die Erweiterung nicht dazwischen angehalten werden kann, z. B. durch Verwendung eines \romannumeral
Erweiterungskontexts. Das Folgende richtet ein Makro ein, das das Zeichen D
als Argumentende-Marker verwendet, aber da es weiter erweitert, bleibt dieser Marker nie im Eingabestrom, sodass er von \lowercase
oder beeinflusst werden könnte \uppercase
:
% first insert a \romannumeral such that the following is expanded as far as possible
\newcommand*\mymacro{\romannumeral\mymacro@a}
% only after \romannumeral has started input the delimiter
\newcommand\mymacro@a[1]{\mymacro@b #1D}
Dann \mymacro@b
könnte das Argument erweiterbar verarbeitet und D
als Trennzeichen verwendet werden. Und Sie könnten den \romannumeral
Erweiterungskontext mit beenden \z@
. Natürlich könnten Sie immer noch \mymacro
einmal erweitern und dann \mymacro@a
mit erweitern, \expandafter
ohne zu beginnen \romannumeral
, sodass (mit ) D
beeinflusst werden könnte , aber zumindest muss dies jetzt mit böswilliger Absicht erstellt werden.\lowercase
\expandafter\expandafter\expandafter\lowercase\expandafter\expandafter\expandafter{\expandafter\expandafter\mymacro{}}
Sie können sich nie vor einer \outer
Neudefinition schützen, aber Leute, die interne Teile des Codes anderer Leute neu definieren, \outer
wollen offenbar keinen funktionierenden Code haben. Daher ist dies vielleicht kein Fall, vor dem Sie sich schützen müssen.