なぜ \expandafter\@firstoftwo という慣用句を使うのでしょうか?

なぜ \expandafter\@firstoftwo という慣用句を使うのでしょうか?

{true}{false}条件テストの後にペアが定義され、結果が次のいずれかになるマクロをよく見かけます。

\expandafter\@firstoftwo

または

\expandafter\@secondoftwo

なぜこれらの s がそこにあるのか? 次のペア\expandafterの最初の中括弧をキャッチするだけだと思っていたのですが?{true}{false}

答え1

は、次のまたは\expandafterを処理します。Ryan がリンクしている質問にあるように、完全なコードは次のようになります。\else\fi

\def\ifeq#1#2{%
 \ifx#1#2\relax
   \expandafter\@firstoftwo
 \else
   \expandafter\@secondoftwo
 \fi
}

何が起こるかを見てみましょう。 を\ifeq\stuff\nonsense{true}{false}ドキュメントに入力します。\ifeqは を吸収し\stuff\nonsense最初の展開後は次のように表示されます。

\ifx\stuff\nonsense\relax
  \expandafter\@firstofone
\else
  \expandafter\@secondoftwo
\fi
{true}{false}

\stuff\nonsense(つまり、条件が真である)と仮定しましょう。すると、 は「真の」パスにあるすべてのものを展開し始めます。これは、次のまたは(ネストを法として)\ifxまでのすべてとして定義されます。重要な点は、それが展開し始めることです。\else\fi初め\elseそして、または を探すために先読みしません\fi。TeX は、そこに到達したときにそれがわかると判断するのです。したがって、次のようになります。

  \expandafter\@firstoftwo
\else
  \expandafter\@secondoftwo
\fi
{true}{false}

TeX は を展開します。これは を越えてを展開する\expandafter効果があります。 を「展開する」とは、ストリームから と一致するものまですべてを削除することを意味します。したがって、次のようになります。\@firstoftwo\else\else\fi

\@firstoftwo{true}{false}

そして、これは単純な に拡張されますtrue

s がなければ\expandafter、次のようになります。

  \@firstoftwo
\else
  \@secondoftwo
\fi
{true}{false}

TeXは真の枝を展開しているので、 を展開します\@firstoftwo。これにより、ストリームから2つのトークン/括弧付きグループが吸収されます。これらは と です\else\@secondoftwoその後、ストリームの最初のトークンが残るので、次のようになります。

\else
\fi
{true}{false}

\else条件に一致するため、TeX はこれを吸収し、ストリームに\fi残るまでのすべてを吸収します。これは私たちが望んでいたことではありません。{true}{false}

要約すると、\expandafters条件付き処理を先に済ませて、結果条件が展開され、条件の結果がストリーム内の次のビットを参照し、未完了の条件から残されたビットを参照しないことが保証されます。

答え2

私は別の観点からこの問題に取り組みます。TeX のプリミティブ条件は条件をテストします。条件は入力ストリームからトークンを吸収するか、条件の真偽が確定するまで吸収しません。したがって、 <IF>プリミティブ条件と、吸収する必要があるトークンのリスト (空の場合もあります) を で表します。たとえば、\ifhmodeはトークンを必要とせず、 2 つ必要です\ifx。場合によっては ( \if、、、、)、TeX はテストに必要な種類のトークンを見つけるために展開を実行しますが、その他の場合 ( 、、、、、、、 )\ifcatは展開を実行しません。したがって、条件と必要なトークンを で表します。\ifnum\ifdim\ifx\ifmmode\ifhmode\ifvmode \ifinner\iftrue\iffalse<IF>拡張が行われ、状態をテストできます。

あなたが言及している典型的な構造は

<IF>
  \expandafter\@firstoftwo
\else
  \expandafter\@secondoftwo
\fi

すでに

\long\def\@firstoftwo#1#2{#1}
\long\def\@secondoftwo#1#2{#2}

より一般的には、

<IF><true>\else<false>\fi

または

\IF<true>\fi

ここで<true>、 と は両方とも<false>空にすることができます。

条件は真である

<IF>TeXは入力ストリームから単に削除し、

<true>\else<false>\fi

または

<true>\fi

条件は偽です

TeXは、何も展開せず\elseに に現れる可能性のあるネストされた条件文を考慮して、次のトークンを探します。したがって、 のネストされた内部に属する はスキップされます。この場合も展開は空であり、 までのすべてのトークンは消えます。一致するものが見つからない場合、TeXはどこかにあるはずの一致するものを探すのを中止します。したがって、2つのケースでは、次のいずれかになります。<true>\else\if...\else\fi<true>\else\else\fi

<false>\fi

または、\elseブランチが存在しない場合は何も表示されません。

これは次の TeX 入力によって証明されます。

\def\showx{\show\x}
\def\showif{\afterassignment\showx
  \expandafter\def\expandafter\x\expandafter}

\showif{\ifvmode<true>\else<false>\fi}
\showif{\ifvmode<true>\fi}
\showif{\ifhmode<true>\else<false>\fi}
\showif{\ifhmode<true>\fi}
\bye

TeX を実行すると、次のトランスクリプトが生成されます。

This is TeX, Version 3.1415926 (TeX Live 2012)
(./plkfi.tex
> \x=macro:
-><true>\else <false>\fi .
\showx ->\show \x

l.5 \showif{\ifvmode<true>\else<false>\fi}

?
> \x=macro:
-><true>\fi .
\showx ->\show \x

l.6 \showif{\ifvmode<true>\fi}

?
> \x=macro:
-><false>\fi .
\showx ->\show \x

l.7 \showif{\ifhmode<true>\else<false>\fi}

?
> \x=macro:
->.
\showx ->\show \x

l.8 \showif{\ifhmode<true>\fi}

?

次は何が起こる

  • の展開は、\else一致するものまですべてを削除し\fi、入力ストリームに何も残さないことで構成されています。ネストされた条件は以前と同様に考慮されます。

  • の展開は\fi空です。

の役割\expandafter

これで、ドロップできる基盤ができました\expandafter

典型的な使用法を見てみましょう:

\def\@ifundefined#1{%
  \expandafter\ifx\csname#1\endcsname\relax
    \expandafter\@firstoftwo
  \else
    \expandafter\@secondoftwo
  \fi}

私たちが望むのは、こう言えることです

\@ifundefined{foo}{T}{F}

したがって、引数を名前として構築されたマクロが比較され\relax(これは実際には興味のない部分です)、その後、true または false の分岐が続きます。

除去後、<IF>私たちは

\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi{T}{F}

そして、TeX は最初のトークンを適切に展開します。これにより、の展開がトリガーされ\else、ここから楽しいことが始まります。

の拡張は、\expandafter(可能であれば) 次のトークンの後のトークンを拡張して消滅させることから構成されます。\else上の規則に従って展開すると、

\@firstoftwo{T}{F}

Tその結果、入力ストリームに残ります。

ここで条件が偽であると仮定します。すると が<IF>までのすべてのものとともに削除され\else

\expandafter\@secondoftwo\fi{T}{F}

今度は\expandafter拡大と消滅の役割を果たします\fi。こうして

\@secondoftwo{T}{F}

それが最終的に残りますF

重要な注意点

\@ifundefined{foo}{T}{F}我々が到達できる場合TFあるいは到達することなく実行中コマンド: マクロ展開のみが使用されています。これにより\@ifundefined、同様に定義されたマクロが 内で使用可能になります\edef:

\edef\test{\@ifundefined{foo}{T}{F}}

は、

\def\test{T}

が定義されている場合\foo\relaxLaTeXでは通常の場合のように と同等ではない)、または

\def\test{F}

が未定義の場合\foo(または と同等の場合\relax)。


がなければ何が起こるでしょうか\expandafter?真の条件付きTeXは次のような問題に直面するでしょう

\@firstoftwo\else\@secondoftwo\fi{T}{F}

そして、 の 2 つの引数は と に\@firstoftwoなります\else\@secondoftwo、これでは何も役に立ちませんよね?

同様に、偽の条件の場合は、

\@secondoftwo\fi{T}{F}

そしてまた物事はうまくいかなくなるでしょう。

答え3

このようなコードの完全なコンテキストは、次のような条件付きマクロにあります。

\def\IfZero#1{%
  \ifnum0=#1\relax
    \expandafter\@firstoftwo
  \else
    \expandafter\@secondoftwo
  \fi
}

\expandafterコードをあまりフォーマットされていない方法で見ると、 は必要です。

\def\IfZero#1{\ifnum0=#1\relax\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi}

(長い行で申し訳ありません) では、TeX 条件ブロックの構造について手がかりがまったく示されていません。 ご覧のとおり、トークン「後」\@firstoftwoは で\else、そのトークンの後ろ\@secondoftwoは で\fi、それぞれ によって展開されます\expandafter。 この愚かな行為の目的は、TeX が を展開するときに条件全体を読み取らないことです\ifnum。正しい true または false ブロックまで前方にスキャンし、そこから続行します。 または\else\fi入力ストリームに残されます。 が展開されると、TeX は条件の最後までスキャンし、入力ストリームの次の部分は に続く内容になります\IfZero

どちらもない場合、と\expandafterによって消費される「2 つ」は、それぞれ、と、および次のマクロ引数になりますが、実際に意図されているもの、つまり の「2 番目」と「3 番目」の「引数」として取られる次の 2 つのマクロ引数ではありません。\@firstoftwo\@secondoftwo\else\@secondoftwo\fi\IfZero

関連情報