low
인수에 아래 첨자를 추가하는 명령을 정의했습니다 .
\newcommand{\low}[1]{{#1}_{l_{\mathcal{A}}}}
그러나 의 인수 low
자체에 첨자가 포함되어 있는 경우(예를 들어 의 경우 \low{\low{\Sigma}}
) 외부에 의해 도입된 첨자가 low
첫 번째 첨자뿐만 아니라 전체 인수에 속한다는 것을 확인하기가 쉽지 않습니다. 따라서 의 인수에 low
아래 첨자가 포함된 경우 자동으로 괄호를 삽입하고 싶습니다 ( \low{\low{\Sigma}}
따라서 \low{(\low{\Sigma})}
.
이를 달성하기 위해 xifthen 패키지를 사용하여 다음을 시도했습니다.
\newcommand{\low}[1]{\ifthenelse{\isin{_}{#1}}{{(#1)}_{l_{\mathcal{A}}}}{{#1}_{l_{\mathcal{A}}}}}
그러나 놀랍게도 이 명령은 가끔 괄호만 삽입합니다. 특히 위에 제공된 예에서는 그렇게 하지 않습니다. 왜 이런 일이 발생하며 이 문제를 해결하려면 어떻게 해야 합니까?
편집: 문제는 \isin
명령 정의를 펼치지 않는 것 같습니다. @egreg는 이미 중첩된 호출을 확인할 수 있는 답변을 제공했지만 \low
아래 첨자가 있는 다른 명령이 포함된 인수에 대해서는 작동하지 않습니다. 임의의 주장에 대해 작동하는 솔루션이 있는 사람이 있습니까?
답변1
어떤 경우에는 "문자열화"를 적용 \protected@edef
하고 확인하기 전에 다음과 같이 하면 문제가 없을 수 있습니다.\@onelevel@sanitize
_
\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
확장 가능한 경우 하나를 제외한 다음 토큰은 확장 가능한 경우 다음 토큰이 확장되기 전에 정확히 한 번 확장됩니다. (La)TeX는 \expandafter
다음 확장이 완료되었지만 하나의 토큰이 완료되면 완료된 작업을 고려합니다 . 따라서 체인/시퀀스를 사용하여 \expandafter
(k+1)번째 토큰을 먼저 확장하기 위해 k 토큰 위로 (La)TeX "점프"할 수 있습니다.
\@onelevel@sanitize\macro
적용하기 전에 "뱉어냈을" 토큰 시퀀스처럼 보이는 범주 코드 12(기타)의 문자 토큰 시퀀스를 뱉어내도록 \macro
의 정의를 변경합니다 . 이는 의 정의 에 대한 각 토큰을 적용하여 얻은 결과를 재정의하는 것과 거의 같습니다 .\macro
\macro
\@onelevel@sanitize
\macro
\string
\macro
\protected@edef
\DeclareRobustCommand
매크로를 정의하지만 그렇게 하기 전에 token 을 통해 정의되거나 앞에 오는 토큰을 제외하고 정의 텍스트의 확장 가능한 모든 토큰을 확장합니다 \protect
. \protected@edef
실제로 할당을 수행하기 전에 정의 텍스트에 포함된 토큰의 정의를 "해제"한다고 말할 수 있습니다 .
\@tempa
"unrolled" 의 모든 정의가 있는 \protected@edef
인수로 확장하여 정의되는 스크래치 매크로입니다 .#1
#1
중괄호에는 일반적으로 특별한 기능이 있으므로 -test는 중괄호 안에 중첩된 항목을 찾지 \ifthenelse{\isin...}
않습니다 . _
따라서 \@onelevel@sanitize
모든 토큰과 중괄호를 테스트를 방해하지 않는 범주 코드 12(기타)의 무해한 일반 문자 토큰으로 변환하는 데 적용됩니다 \ifthenelse{\isin...}
.
그리고 다음을 통해 카테고리 코드 12(기타)의 (문자열화된) 밑줄을 확인하지 않는 루틴이 있습니다.xifthen의 \ifthenelse{\isin...}
-thingie이지만 문자열화 없이 카테고리 코드 8(아래 첨자)의 토큰을 확인합니다.
루틴은 인수를 형성하는 토큰을 검사할 때 반복적으로 자신을 호출합니다.
루틴은 여전히 인수를 확장하지 않습니다. 이는 여전히 를 통해 수행되어야 합니다 \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}
이 루틴은 카테고리 코드 12(기타)의 문자 토큰을 감지(문자열화)하는 것이 아니라 _
카테고리 코드 8(아래 첨자)의 모든 문자 토큰(명시적이든 암시적이든)을 감지하는 것을 목표로 합니다.
답변2
아이디어(간단한 해결책은 아님)는 인수를 상자 안에 배치하고 괄호가 필요할 만큼 크지는 않지만 \Sigma에 괄호를 추가하기에는 너무 짧지 않은 문자의 높이로 높이를 확인하는 것입니다.
그리고 우리의 기본 인수가 무엇인지 추측해 보세요: \Sigma
... P
코드(일부 테스트 포함)는 다음과 같습니다.
\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}
이는 다음을 생성합니다.
추신:물론 특별한 경우에는 수동 솔루션을 추가해야 하지만 어쨌든 명령에 많은 경우에 예외가 있을 것이라고 확신합니다.