Почему используется идиома \expandafter\@firstoftwo?

Почему используется идиома \expandafter\@firstoftwo?

Я вижу много макросов, которые представляют собой условные проверки, за которыми следует {true}{false}пара, определенная таким образом, что они приводят к одному из следующих результатов:

\expandafter\@firstoftwo

или

\expandafter\@secondoftwo

Почему эти \expandafters там? Я бы подумал, что они просто поймают первую скобку в следующей {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.

Без буквы \expandafters мы получаем:

  \@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
}

Символы \expandafters необходимы, если вы смотрите на код в менее форматированном виде:

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

(извините за длинную строку), в которой нет подсказки о структуре условных блоков TeX. Как вы можете видеть, токен "after" \@firstoftwo— это \else, а after \@secondoftwo— это \fi, которые соответственно расширяются на \expandafter. Цель этой глупости в том, что TeX не читает все условное выражение, когда расширяет \ifnum; он просто сканирует вперед до правильного блока true или false и продолжает оттуда. \elseor \fiостаются во входном потоке! После того, как они расширяются, TeX сканирует до конца условного выражения, и следующим во входном потоке становится то, что следует за \IfZero.

Без любого из них \expandafter«два», потребляемые \@firstoftwoи \@secondoftwo, были бы соответственно \else\@secondoftwoи \fiи следующим макроаргументом, а не тем, что на самом деле подразумевается, а именно двумя следующими макроаргументами, которые принимаются как «второй» и «третий» «аргументы» \IfZero.

Связанный контент