コマンド引数の添え字の検出

コマンド引数の添え字の検出

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により、次のトークンが展開可能であれば、その前に次のトークンが正確に 1 回展開されます。(La)TeX は、次のトークンの展開が完了した時点で の作業が完了したとみなします。したがって、 (La)TeX が最初に (k+1) 番目のトークンを展開するために k 個のトークンを「ジャンプ」するように の\expandafterチェーン/シーケンスを使用できます。\expandafter

\@onelevel@sanitize\macroの定義を変更する\macroと、 は、を適用する前に\macroによって「吐き出された」トークン シーケンスに似た、カテゴリ コード 12(その他) の文字トークンのシーケンスを吐き出します。これは、 の定義の各トークンに を適用して得られるものを に再定義することとほぼ同じです。\macro\@onelevel@sanitize\macro\string\macro

\protected@edefマクロを定義しますが、定義する前に、トークン を介して定義されて\DeclareRobustCommandいるか、トークンが先行しているトークンを除いて、定義テキストのすべての展開可能なトークンを展開します\protect。次のように言うこともできます。\protected@edef割り当てを実際に実行する前に、定義テキストに含まれるトークンの定義を「展開」します。

\@tempaは、すべての定義が「展開」された\protected@edef引数に展開するために によって定義されるスクラッチマクロです。#1#1

-testは、中括弧にネストされているものを\ifthenelse{\isin...}検出しません。これは、中括弧が通常特別な機能を持っているためです。したがって、すべてのトークン (および中括弧も) を、テストを妨げないカテゴリ コード 12 (その他) の通常の無害な文字トークンに変換するために適用されます。_\@onelevel@sanitize\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

1 つのアイデア (簡単な解決策ではありません) は、引数をボックス内に配置し、その高さを、括弧を必要とするほど高くなく、\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}

これにより、次のようになります。

ここに画像の説明を入力してください

PS: もちろん、特別な場合には手動ソリューションを追加する必要がありますが、いずれにしても、コマンドでは多くの場合に例外があるはずです。

関連情報