Я вижу много макросов, которые представляют собой условные проверки, за которыми следует {true}{false}
пара, определенная таким образом, что они приводят к одному из следующих результатов:
\expandafter\@firstoftwo
или
\expandafter\@secondoftwo
Почему эти \expandafter
s там? Я бы подумал, что они просто поймают первую скобку в следующей {true}{false}
паре?
решение1
S \expandafter
должны иметь дело со следующим \else
или \fi
. Как и в вопросе, на который ссылается Райан, полный код выглядит примерно так:
\def\ifeq#1#2{%
\ifx#1#2\relax
\expandafter\@firstoftwo
\else
\expandafter\@secondoftwo
\fi
}
Давайте проследим, что происходит. Мы помещаем \ifeq\stuff\nonsense{true}{false}
в наш документ. The \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
.
Без буквы \expandafter
s мы получаем:
\@firstoftwo
\else
\@secondoftwo
\fi
{true}{false}
TeX все еще расширяет истинную ветвь, поэтому расширяет \@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
исчезнет. В случае, если соответствие не \else
найдено, TeX прекратит искать соответствие \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
без когда-либовыполнениекоманда: просто макрорасширение было использовано. Это делает \@ifundefined
и аналогично определенный макрос применимым внутри \edef
:
\edef\test{\@ifundefined{foo}{T}{F}}
будет эквивалентно
\def\test{T}
в случае , если \foo
определено (и не эквивалентно \relax
, как обычно в LaTeX) или
\def\test{F}
если \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
or \fi
остаются во входном потоке! После того, как они расширяются, TeX сканирует до конца условного выражения, и следующим во входном потоке становится то, что следует за \IfZero
.
Без любого из них \expandafter
«два», потребляемые \@firstoftwo
и \@secondoftwo
, были бы соответственно \else\@secondoftwo
и \fi
и следующим макроаргументом, а не тем, что на самом деле подразумевается, а именно двумя следующими макроаргументами, которые принимаются как «второй» и «третий» «аргументы» \IfZero
.