Ich habe einen Befehl definiert low
, der einem Argument einen Index hinzufügt:
\newcommand{\low}[1]{{#1}_{l_{\mathcal{A}}}}
Wenn das Argument von jedoch low
selbst Indizes enthält (z. B. im Fall von \low{\low{\Sigma}}
), ist es nicht leicht zu erkennen, dass die durch das äußere eingeführten Indizes low
zum gesamten Argument gehören und nicht nur zum ersten Index. Daher möchte ich automatisch Klammern einführen, wenn das Argument von low
einen Index enthält ( \low{\low{\Sigma}}
sollte also wie aussehen) \low{(\low{\Sigma})}
.
Um dies zu erreichen, habe ich mit dem xifthen-Paket Folgendes versucht:
\newcommand{\low}[1]{\ifthenelse{\isin{_}{#1}}{{(#1)}_{l_{\mathcal{A}}}}{{#1}_{l_{\mathcal{A}}}}}
Zu meiner Überraschung fügt dieser Befehl die Klammern jedoch nur manchmal ein. Insbesondere im oben angegebenen Beispiel ist dies nicht der Fall. Warum passiert das und was kann ich tun, um das Problem zu beheben?
EDIT: Das Problem scheint darin zu liegen, dass \isin
die Definitionen der Befehle nicht aufgerollt werden. @egreg hat bereits eine Antwort bereitgestellt, mit der ich nach verschachtelten Aufrufen suchen kann, die \low
aber nicht für Argumente funktioniert, die andere Befehle mit Indizes enthalten. Hat jemand eine Lösung, die für beliebige Argumente funktioniert?
Antwort1
In manchen Fällen kann es hilfreich sein, sich zu bewerben \protected@edef
und \@onelevel@sanitize
vorher nach einer „Stringifizierung“ zu suchen :_
\documentclass{article}
\usepackage{amsmath}
\usepackage{xifthen}
\makeatletter
\DeclareRobustCommand\DetectUnderscore[1]{%
\begingroup
\protected@edef\@tempa{#1}%
\@onelevel@sanitize\@tempa
\expandafter\expandafter\expandafter\endgroup
\expandafter\expandafter\expandafter\ifthenelse
\expandafter\expandafter\expandafter{%
\expandafter\expandafter\expandafter\isin
\expandafter\expandafter\expandafter{%
\expandafter\expandafter\string_%
\expandafter}%
\expandafter{%
\@tempa}}{{(#1)}}{{#1}}%
}%
\newcommand\low[1]{%
\DetectUnderscore{#1}%
_{l_{\mathcal{A}}}%
}
\makeatother
\begin{document}
\[
\low{\Sigma} \qquad
\low{\low{\Sigma}} \qquad
\low{\low{\low{\Sigma}}} \qquad
\]
\[
\low{\Sigma_b} \qquad
\low{\low{\Sigma_b}} \qquad
\low{\low{\low{\Sigma_b}}} \qquad
\]
\[
\low{{\Sigma_b}} \qquad
\low{b_{\low{c_{\low{\Sigma_d}}}}} \qquad
(\Sigma_b)_{l_\mathcal{A}}
\]
\[
\low{\low{1} + 2}
\]
\end{document}
\expandafter
bewirkt, dass das übernächste Token – sofern erweiterbar – genau einmal erweitert wird, bevor das nächste Token erweitert wird, sofern es erweiterbar ist. (La)TeX betrachtet die Arbeit als \expandafter
erledigt, wenn die Erweiterung des übernächsten Tokens abgeschlossen ist. Daher können Sie Ketten/Sequenzen von verwenden, \expandafter
um (La)TeX über k Token „springen“ zu lassen, um zuerst das (k+1)-te Token zu erweitern.
\@onelevel@sanitize\macro
ändert die Definition von , \macro
sodass \macro
eine Folge von Zeichentokens mit dem Kategoriecode 12 (andere) ausgegeben wird, die wie die Tokenfolge aussieht, die \macro
vor der Anwendung von "ausgespuckt" worden wäre \@onelevel@sanitize
. Es ist fast so, als würde man neu definieren \macro
, was man erhält, wenn man \string
auf jedes Token der \macro
Definition von anwendet.
\protected@edef
definiert ein Makro, expandiert aber vorher alle erweiterbaren Token des Definitionstexts außer denen, die entweder über das Token definiert sind \DeclareRobustCommand
oder denen das Token vorangestellt ist \protect
. Man könnte auch sagen: \protected@edef
„entrollt“ die Definitionen der in seinem Definitionstext enthaltenen Token, bevor die Zuweisung tatsächlich ausgeführt wird.
\@tempa
ist ein Scratch-Makro, das mittels _definiert wird, um \protected@edef
das Argument #1
mit allen Definitionen in #1
"unrolled" zu erweitern.
Der \ifthenelse{\isin...}
-Test findet nicht _
, dass in geschweiften Klammern verschachtelte Zeichen vorkommen, da geschweifte Klammern normalerweise eine spezielle Funktion haben. Daher wird angewendet, um alle Token, und damit auch die geschweiften Klammern, in normale, harmlose Zeichen-Token des Kategoriecodes 12 (andere) umzuwandeln, die den Test \@onelevel@sanitize
nicht stören .\ifthenelse{\isin...}
Und hier ist eine Routine, die nicht auf (stringifizierten) Unterstrich des Kategoriecodes 12 (andere) prüft, überxwenndann's \ifthenelse{\isin...}
-Ding, prüft aber auf Token des Kategoriecodes 8 (Index) ohne Stringifizierung.
Die Routine ruft sich selbst rekursiv auf, wenn sie die Token untersucht, die das Argument bilden.
Die Routine erweitert das Argument immer noch nicht – dies muss immer noch über erfolgen \protected@edef
.
\documentclass{article}
\usepackage{amsmath}
\makeatletter
%%-----------------------------------------------------------------------------
%% Paraphernalia ;-) :
%%.............................................................................
\newcommand\UD@firstoftwo[2]{#1}%
\newcommand\UD@secondoftwo[2]{#2}%
\newcommand\UD@exchange[2]{#2#1}%
\newcommand\UD@removespace{}\UD@firstoftwo{\def\UD@removespace}{} {}%
%%-----------------------------------------------------------------------------
%% Extract first inner undelimited argument:
%%.............................................................................
%% \UD@ExtractFirstArg{ABCDE} yields {A}
%%
%% \UD@ExtractFirstArg{{AB}CDE} yields {AB}
%%
%% !!! The argument of \UD@ExtractFirstArg must not be empty. !!!
%% You can check for emptiness via \UD@CheckWhetherNull before applying
%% \UD@ExtractFirstArg.
%%.............................................................................
\newcommand\UD@RemoveTillUD@SelDOm{}%
\long\def\UD@RemoveTillUD@SelDOm#1#2\UD@SelDOm{{#1}}%
\newcommand\UD@ExtractFirstArg[1]{%
\romannumeral0%
\UD@ExtractFirstArgLoop{#1\UD@SelDOm}%
}%
\newcommand\UD@ExtractFirstArgLoop[1]{%
\expandafter\UD@CheckWhetherNull\expandafter{\UD@firstoftwo{}#1}%
{ #1}%
{\expandafter\UD@ExtractFirstArgLoop\expandafter{\UD@RemoveTillUD@SelDOm#1}}%
}%
%%-----------------------------------------------------------------------------
%% 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>}%
%% The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
%% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
%%.............................................................................
\newcommand\UD@CheckWhetherNull[1]{%
\romannumeral0\expandafter\UD@secondoftwo\string{\expandafter
\UD@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
\UD@secondoftwo\string}\expandafter\UD@firstoftwo\expandafter{\expandafter
\UD@secondoftwo\string}\expandafter\expandafter\UD@firstoftwo{ }{}%
\UD@secondoftwo}{\expandafter\expandafter\UD@firstoftwo{ }{}\UD@firstoftwo}%
}%
%%-----------------------------------------------------------------------------
%% Check whether argument's first token is a catcode-1-character
%%.............................................................................
%% \UD@CheckWhetherBrace{<Argument which is to be checked>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked has leading
%% catcode-1-token>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked has no leading
%% catcode-1-token>}%
\newcommand\UD@CheckWhetherBrace[1]{%
\romannumeral0\expandafter\UD@secondoftwo\expandafter{\expandafter{%
\string#1.}\expandafter\UD@firstoftwo\expandafter{\expandafter
\UD@secondoftwo\string}\expandafter\expandafter\UD@firstoftwo{ }{}%
\UD@firstoftwo}{\expandafter\expandafter\UD@firstoftwo{ }{}\UD@secondoftwo}%
}%
%%-----------------------------------------------------------------------------
%% Check whether brace-balanced argument starts with a space-token
%%.............................................................................
%% \UD@CheckWhetherLeadingSpace{<Argument which is to be checked>}%
%% {<Tokens to be delivered in case <argument
%% which is to be checked>'s 1st token is a
%% space-token>}%
%% {<Tokens to be delivered in case <argument
%% which is to be checked>'s 1st token is not
%% a space-token>}%
\newcommand\UD@CheckWhetherLeadingSpace[1]{%
\romannumeral0\UD@CheckWhetherNull{#1}%
{\expandafter\expandafter\UD@firstoftwo{ }{}\UD@secondoftwo}%
{\expandafter\UD@secondoftwo\string{\UD@CheckWhetherLeadingSpaceB.#1 }{}}%
}%
\newcommand\UD@CheckWhetherLeadingSpaceB{}%
\long\def\UD@CheckWhetherLeadingSpaceB#1 {%
\expandafter\UD@CheckWhetherNull\expandafter{\UD@secondoftwo#1{}}%
{\UD@exchange{\UD@firstoftwo}}{\UD@exchange{\UD@secondoftwo}}%
{\UD@exchange{ }{\expandafter\expandafter\expandafter\expandafter
\expandafter\expandafter\expandafter}\expandafter\expandafter
\expandafter}\expandafter\UD@secondoftwo\expandafter{\string}%
}%
%%.............................................................................
%% Check whether brace-balanced argument starts with a token of
%% category code 8 (subscript)
%%.............................................................................
%% \UD@CheckWhetherFirstTokenHasCatcodeSubscript{<Argument which is to be checked>}%
%% {<Tokens to be delivered in case that
%% <argument which is to be checked> has a first
%% token of catcode 8>}%
%% {<Tokens to be delivered in case that
%% <argument which is to be checked> does not have
%% a first token of catcode 8>}%
%%
\newcommand\UD@CheckWhetherFirstTokenHasCatcodeSubscript[1]{%
\romannumeral0%
\UD@CheckWhetherNull{#1}{\UD@exchange{ }{\expandafter}\UD@secondoftwo}{%
\UD@CheckWhetherBrace{#1}{\UD@exchange{ }{\expandafter}\UD@secondoftwo}{%
\UD@CheckWhetherLeadingSpace{#1}{\UD@exchange{ }{\expandafter}\UD@secondoftwo}{%
\expandafter\expandafter\expandafter\UD@@CheckWhetherFirstTokenHasCatcodeSubscript
\UD@ExtractFirstArg{#1}%
}%
}%
}%
}%
\newcommand\UD@@CheckWhetherFirstTokenHasCatcodeSubscript[1]{%
\expandafter\ifcat_#1%
\expandafter\UD@firstoftwo\else\expandafter\UD@secondoftwo\fi
{\UD@exchange{ }{\expandafter}\UD@firstoftwo}%
{\UD@exchange{ }{\expandafter}\UD@secondoftwo}%
}%
%%-----------------------------------------------------------------------------
%% Check whether argument does contain underscore/tokens of
%% category code 8 (subscript), no matter if nested in braces or not.
%%
%% \UD@CheckWhetherSubscriptTokens{<Argument which is to be checked>}%
%% {<Tokens to be delivered in case that
%% <argument which is to be checked> contains
%% some token(s) of catcode 8 (subscript)>}%
%% {<Tokens to be delivered in case that
%% <argument which is to be checked> contains
%% no token of catcode 8 (subscript)>}%
%%
%%-----------------------------------------------------------------------------
\newcommand\UD@CheckWhetherSubscriptTokens{\romannumeral0\UD@@CheckWhetherSubscriptTokens}%
\newcommand\UD@@CheckWhetherSubscriptTokens[3]{%
\UD@CheckWhetherNull{#1}{ #3}{%
\UD@CheckWhetherLeadingSpace{#1}{%
\expandafter\UD@@CheckWhetherSubscriptTokens\expandafter{\UD@removespace#1}{#2}{#3}%
}{%
\UD@CheckWhetherBrace{#1}{%
\expandafter\expandafter\expandafter\UD@CheckWhetherSubscriptTokens
\UD@ExtractFirstArg{#1}{ #2}{%
\expandafter\UD@@CheckWhetherSubscriptTokens\expandafter{\UD@firstoftwo{}#1}{#2}{#3}%
}%
}{%
\UD@CheckWhetherFirstTokenHasCatcodeSubscript{#1}{ #2}{%
\expandafter\UD@@CheckWhetherSubscriptTokens\expandafter{\UD@firstoftwo{}#1}{#2}{#3}%
}%
}%
}%
}%
}%
\DeclareRobustCommand\UD@CallUD@CheckWhetherSubscriptTokensOnExpansion[1]{%
\begingroup
\protected@edef\@tempa{#1}%
\expandafter\endgroup
\expandafter\UD@CheckWhetherSubscriptTokens\expandafter{\@tempa}{{(#1)}}{{#1}}%
}%
\newcommand\low[1]{%
\UD@CallUD@CheckWhetherSubscriptTokensOnExpansion{#1}_{l_{\mathcal{A}}}%
}
\makeatother
\begin{document}
% Let`s use | and \myunderscore in the same way as _ :
\catcode`\|=8
\let\myunderscore=_
\[
\low{\Sigma} \qquad
\low{\low{\Sigma}} \qquad
\low{\low{\low{\Sigma}}} \qquad
\]
\[
\low{\Sigma\myunderscore b} \qquad
\low{\low{\Sigma\myunderscore b}} \qquad
\low{\low{\low{\Sigma|b}}} \qquad
\]
\[
\low{{\Sigma_b}} \qquad
\low{b_{\low{c_{\low{\Sigma|d}}}}} \qquad
(\Sigma_b)_{l_\mathcal{A}}
\]
\[
\low{\low{1} + 2}
\]
\end{document}
Beachten Sie, dass diese Routine nicht auf die Erkennung von (stringifizierten) _
Zeichen-Tokens des Kategoriecodes 12 (Sonstiges) abzielt, sondern auf die Erkennung aller Zeichen-Tokens (seien sie explizit oder implizit) des Kategoriecodes 8 (Index).
Antwort2
Eine Idee (keine direkte Lösung) besteht darin, das Argument in ein Feld zu setzen und seine Höhe mit der Höhe eines Zeichens zu vergleichen, von dem Sie annehmen, dass es nicht zu hoch ist, um Klammern zu benötigen, aber auch nicht zu niedrig, um Ihrem \Sigma Klammern hinzuzufügen.
Und raten Sie mal, was unser Standardargument sein wird: \Sigma
... P
Der Code (der einige Tests enthält) ist dieser:
\documentclass{article}
\def\DefLowArg{$\Sigma$}
\let\oldDefLowArg\DefLowArg
\newsavebox{\myAbox}
\newsavebox{\myBbox}
\newcommand{\low}[2][\DefLowArg]{\savebox\myAbox{\vbox{#1}}\savebox\myBbox{\vbox{\ensuremath{#2}}}
\ifdim\dimexpr\ht\myAbox+\dp\myAbox<\dimexpr\ht\myBbox+\dp\myBbox\relax
\left({#2}\right)_{l_{\mathcal{A}}}
\else {#2}_{l_{\mathcal{A}}}\fi
}
\begin{document}
\[\low{\low{\Sigma}}\]
\[\low{\Sigma}\]
\[
\low{\sum_{i=3}^5 F(x)}
\]
\[
\low{\frac{F(x)}{x+5}}
\]
\[\low{F_x}\]
\[\low[1/4]{F(x)}\]
\[\low{x^2}\]
\[
\low{G_x}
\]
These commands may be should add without parentheses
\[
\low{g(z)}
\]
\[
\low{F(x)}
\]
{\bfseries Solution 1 Add an tall optional argument in the command like: \verb|\low[/]{F(x)}|}
\[
\low[/]{g(z)}
\]
\[
\low[/]{F(x)}
\]
{\bfseries Solution 2 Change the Default argument \verb|\DefLowArg| to something tall enough (return with \verb|\let\DefLowArg\oldDefLowArg|):}
\xdef\DefLowArg{/}
\[
\low{g(z)}
\]
\[
\low{F(x)}
\]
\let\DefLowArg\oldDefLowArg
{\bfseries And back to default}
\[
\low{F(X)}
\]
\end{document}
Das Ergebnis:
PS: Natürlich sollten in Sonderfällen manuelle Lösungen hinzugefügt werden, aber ich bin sicher, dass es in Ihrem Kommando ohnehin für viele Fälle Ausnahmen gibt.