%20%E5%AE%9A%E7%BE%A9%E3%81%A7%E3%81%8D%E3%81%AA%E3%81%84%E3%83%88%E3%83%BC%E3%82%AF%E3%83%B3%E3%81%AF%E3%81%82%E3%82%8A%E3%81%BE%E3%81%99%E3%81%8B%3F.png)
\uppercase
/の影響を受け\lowercase
ず、 に関して(再)定義できないトークンはありますか\outer
?
\uppercase
(もしそうなら、 /内で使用される可能性のあるものとの引数区切り文字として使用したいと思います\lowercase
。)
答え1
私の知る限り、そのようなトークンは存在しません。
各トークンは、次の 2 つのトークン クラスのうち少なくとも 1 つに属します。アクティブ キャラクタ トークンは、両方のクラスに同時に属します。
- 制御シーケンス。(制御ワード トークン、制御シンボル トークン、アクティブ文字トークン) すべての制御シーケンスは、 に関して (再) 定義可能です
\outer
。 - 明示的な文字トークン。明示的な文字トークンはそれぞれ
\uppercase
/の影響を受けます\lowercase
(\uccode
/が\lccode
適切に設定されている場合)。
多くの場合、区切りのない引数を使用して空かどうかをチェックすることで、区切りのある引数を完全に回避できます。
たとえば、中括弧でバランスが取れたトークンリストから最初の無制限の引数を抽出するために、次のようなものを使用することが多いです。
%% \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
定義は
%%-----------------------------------------------------------------------------
%% 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}%
}%
または
\newcommand\UD@CheckWhetherNull[1]{%
\romanumeral\ifcat$\detokenize{#1}$%
\expandafter\expandafter\expandafter\z@\expandafter\@firstoftwo\else
\expandafter\expandafter\expandafter\z@\expandafter\@secondoftwo\fi
}%
。 )
これにより、次のものが得られます。
\romannumeral\UD@ExtractFirstArgLoop{{A}{B}CDE\UD@SelDOm}%
→ A
.
最初の区切られていない引数以外のすべてを削除するのに必要な反復回数を減らすために、 で区切られた引数を使用しました。(必要な反復回数は、「中括弧で囲まれていない引数内の の\UD@SelDOm
数」+1 です)。\UD@SelDOm
\UD@SelDOm
で定義されている可能性のある - 区切り文字が気に入らない場合は\outer
、次のように、結果を得るまでにより多くの反復処理が必要になりますが、これを使わずに済みます。必要な反復処理の回数は、「引数内の、括弧で囲まれていない区切りのない引数の数」+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}}%
}%
例えば、
\romannumeral\UD@ExtractFirstArgLoop{{A}{BC}DE{}}%
→A
区切られた引数を完全に回避するために同様のことを実行できる状況は数多くあります。
時々、問題の引数を処理するマクロトークンを引数区切り文字として使用することがあり\def\macro#1\macro{...}
ます。たとえば、 。これが実行可能/合理的かどうかは、 が\macro
独自の引数内にネストされて誤って区切り文字と一致してしまう可能性があるかどうか、または のようなことが起こる可能性があるかどうかという質問によって決まります\let\macrob=\macro \outer\def\macro...
。
マクロ引数を安全にする方法についてはすでに考えていると思いますが、\outer\def...
and \uppercase
/以外にも罠があることを指摘しておきます\lowercase
。
たとえば、ユーザーによって指定された (ほぼ) 任意の引数を処理する、区切られた引数に基づくマクロ メカニズムが、表形式の環境内またはアライメント内で動作するかどうかという疑問があります。
\grabdelimited
たとえば、区切られた引数を処理し、ユーザーが文字B
とを収集してトークン化解除するために使用するマクロを想定します&
。\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}
表形式環境内の最初の/コメント行ではエラーが発生しますが、2 番目の行ではエラーは発生しません。これは、
&
区切られた引数に属するものが の中括弧内に隠されているため です\@firstofone
。さらに別の問題としては、不均衡な
\if...
/\else
/ を\fi
マクロ引数として渡すことで、それらの引数を処理するマクロの定義で発生する一部の\if...
/と誤って一致する可能性があることが考えられます。\else
\csname
不均衡な/も同様です\endcsname
。
答え2
拡張コメント:
引数終了マーカーが\uppercase
/の影響を受けるのではないかと心配な場合は\lowercase
、 / がトップレベルに現れないようにして、 または に表示されないようにする必要があります\uppercase
。\lowercase
これは、たとえば展開コンテキストを使用して、展開が途中で停止できないコンテキストにのみ挿入されるようにすることで実現できます\romannumeral
。次のマクロは、文字をD
引数終了マーカーとして使用するマクロを設定しますが、さらに展開されるため、そのマーカーは入力ストリームに残されず、\lowercase
またはの影響を受けることはありません\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}
次に、\mymacro@b
引数を展開可能な方法で処理し、D
を区切り文字として使用できます。また、\romannumeral
で展開コンテキストを終了できます\z@
。もちろん、 を開始せずにを\mymacro
一度展開してから を展開して、\mymacro@a
が( を使用)の影響を受けるようにすることもできますが、少なくとも、これは悪意を持って作成されたものでなければなりません。\expandafter
\romannumeral
D
\lowercase
\expandafter\expandafter\expandafter\lowercase\expandafter\expandafter\expandafter{\expandafter\expandafter\mymacro{}}
再定義から身を守ることはできません\outer
が、他の人のコードの内部を再定義する人は、\outer
動作するコードを望んでいないようですので、これは身を守る必要のないケースかもしれません。