我看到很多宏都是條件測試,然後是一{true}{false}
對定義的宏,因此它們會導致:
\expandafter\@firstoftwo
或者
\expandafter\@secondoftwo
為什麼\expandafter
會有這些?我本來希望他們會抓住下一{true}{false}
對的第一個括號?
答案1
s\expandafter
用於處理以下\else
或\fi
。正如 Ryan 連結到的問題一樣,完整的程式碼類似於:
\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
or \fi
。 TeX 認為當它到達時它就會知道它。所以我們有:
\expandafter\@firstoftwo
\else
\expandafter\@secondoftwo
\fi
{true}{false}
TeX 現在擴展了它\expandafter
。這具有\@firstoftwo
超越\else
並擴展它的效果。 「擴展」意味著從流中\else
刪除它以及直到匹配的所有內容。\fi
所以我們剩下:
\@firstoftwo{true}{false}
然後這被擴展到簡單的true
.
如果沒有\expandafter
s,我們會得到:
\@firstoftwo
\else
\@secondoftwo
\fi
{true}{false}
TeX 仍在擴展 true 分支,因此擴展\@firstoftwo
。這會從流中吸收兩個令牌/支撐組。這些恰好是\else
和\@secondoftwo
。然後它留下流中的第一個,所以我們得到
\else
\fi
{true}{false}
與\else
條件匹配,因此 TeX 吸收此內容以及直到流中\fi
留下的所有內容。{true}{false}
這不是我們想要的。
總之,要在執行\expandafters
前排除條件處理。結果條件語句的內容被擴展,從而確保條件語句的結果看到流中的下一個位,而不是未完成的條件語句中留下的位。
答案2
我會從不同的角度來攻擊這個問題。 TeX 中的原始條件測試條件,它可以從輸入流中吸收標記,也可以不吸收標記,直到可以確定條件的真假。因此,讓我們用
<IF>
原始條件和必須吸收的標記清單(可能為空)來表示。例如\ifhmode
不需要令牌,\ifx
需要兩個。在某些情況下 ( \if
, \ifcat
, \ifnum
, \ifdim
) TeX 執行擴展以便找到測試所需的標記類型;在其他 ( \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>
以為空。
條件為真
TeX 將簡單地<IF>
從輸入流中刪除,留下
<true>\else<false>\fi
或者
<true>\fi
條件為假
TeX 尋找下一個\else
標記,同時考慮可能出現的巢狀條件<true>
而不展開任何內容。因此,\else
屬於嵌套\if...\else\fi
內部的一個<true>
將被跳過。在這種情況下,擴展也是空的,並且所有標記\else
都會消失。如果沒有找到匹配,TeX 將停止尋找最好位於某處的\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}
在我們能夠達到T
或F
不達到的情況下執行a 指令:剛剛使用了巨集擴充。這使得\@ifundefined
類似定義的巨集可以在內部使用\edef
:
\edef\test{\@ifundefined{foo}{T}{F}}
將相當於
\def\test{T}
in case\foo
被定義(並不等同於\relax
LaTeX 中通常的 )或
\def\test{F}
if\foo
未定義(或相當於\relax
)。
如果沒有的話會發生什麼事\expandafter
?對於真正的條件 TeX 將面臨
\@firstoftwo\else\@secondoftwo\fi{T}{F}
的兩個參數將\@firstoftwo
是\else
和\@secondoftwo
,這不會做任何有用的事情,不是嗎?
類似地,對於錯誤的條件,我們會得到
\@secondoftwo\fi{T}{F}
事情又會出錯。
答案3
此類程式碼的完整上下文位於條件宏中,例如
\def\IfZero#1{%
\ifnum0=#1\relax
\expandafter\@firstoftwo
\else
\expandafter\@secondoftwo
\fi
}
\expandafter
如果您以較少格式的方式查看程式碼,則 s 是必需的:
\def\IfZero#1{\ifnum0=#1\relax\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi}
(對長行表示歉意)其中沒有給出有關 TeX 條件塊結構的線索。正如您所看到的,標記“after”\@firstoftwo
是\else
,而 after\@secondoftwo
是\fi
,它們分別由 展開\expandafter
。這種愚蠢的目的是 TeX 在擴展\ifnum
;時不會讀取整個條件。它只是向前掃描到正確的 true 或 false 區塊並從那裡繼續。或留在輸入流中\else
!\fi
一旦它們被擴展,TeX 就會掃描到條件的末尾,輸入流中的下一個內容就是 後面的內容\IfZero
。
如果沒有任一,和\expandafter
消耗的「兩個」將分別是和以及後面的宏參數,而不是實際意圖,即下面的兩個宏參數被視為「第二個」和「第三個」參數」的。\@firstoftwo
\@secondoftwo
\else\@secondoftwo
\fi
\IfZero