> For the complete documentation index, see [llms.txt](https://overleaf-pro.ayaka.space/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://overleaf-pro.ayaka.space/latex/ru/podrobnye-stati/18-how-does-expandafter-work-a-detailed-study-of-consecutive-expandafter-commands.md).

# Как работает \\\expandafter: подробное исследование последовательных команд \\\expandafter

&#x20;[Часть 1](/latex/ru/podrobnye-stati/19-how-does-expandafter-work-an-introduction-to-tex-tokens.md)   [Часть 2](/latex/ru/podrobnye-stati/22-how-does-expandafter-work-the-meaning-of-expansion.md)   [Часть 3](/latex/ru/podrobnye-stati/21-how-does-expandafter-work-tex-uses-temporary-token-lists.md)   [Часть 4](/latex/ru/podrobnye-stati/20-how-does-expandafter-work-from-basic-principles-to-exploring-tex-s-source-code.md)   [Часть 5](/latex/ru/podrobnye-stati/17-how-does-expandafter-work-a-detailed-macro-case-study.md)   [Часть 6](/latex/ru/podrobnye-stati/18-how-does-expandafter-work-a-detailed-study-of-consecutive-expandafter-commands.md)&#x20;

## Разбор случая: понимание \expandafter\expandafter\expandafter...

Продвинутые макросы, такие как те, что встречаются в пакетах LaTeX, часто широко используют несколько подряд идущих `\expandafter` команд для выполнения сложной «жонглировки» токенами. Для большинства из нас такие макросы могут быть трудными для понимания или пугающими в написании. В этом разделе мы рассмотрим механику, лежащую в основе обработки TeX нескольких подряд идущих `\expandafter` команд:

```
\expandafter\expandafter\expandafter...
```

Чтобы помочь нашему объяснению, мы добавим индексы к каждому `\expandafter`— чтобы обозначить, о каком именно мы говорим:

```
        \expandafter1\expandafter2\expandafter3...
```

Кроме того, мы расширим обозначения для токенов, обрабатываемых каждым `\expandafter` использовать $$\mathrm{T^i\_1}$$ и $$\mathrm{T^i\_2}$$ обозначающие токены $$\mathrm{T\_1}$$ и $$\mathrm{T\_2}$$ считанные `\expandafter` с индексом `i`: `\expandafter`<sub>i</sub> $$\mathrm{T^i\_1T^i\_2}$$. Кроме того, мы также предположим, что два токена $$\mathrm{T\_X}$$ и $$\mathrm{T\_Y}$$ следуют после `\expandafter<sub>3</sub>` так что наш ввод выглядит так: `\expandafter<sub>1</sub>\expandafter<sub>2</sub>\expandafter<sub>3</sub>` $$\mathrm{T\_{X}T\_{Y}}$$.

Когда TeX начинает обрабатывать этот ввод, для `\expandafter<sub>1</sub>` он увидит

* $$\mathrm{T^1\_1} =$$`\expandafter<sub>2</sub>` который сохраняется для последующего *повторного вставления* обратно во ввод
* $$\mathrm{T^1\_2} =$$`\expandafter<sub>3</sub>`, который разворачивается

Если мы вернёмся к нашему более раннему обсуждению [кода внутри TeX](/latex/ru/podrobnye-stati/20-how-does-expandafter-work-from-basic-principles-to-exploring-tex-s-source-code.md) который реализует `\expandafter`, именно при разворачивании `\expandafter<sub>3</sub>` что мы видим протекание рекурсии. Чтобы обработать `\expandafter<sub>1</sub>` TeX уже вызвал свою внутреннюю функцию `expand()`, так что для обработки (разворачивания) `\expandafter<sub>3</sub>` TeX совершает *вторую* вызов `expand()`—изнутри `expand()` самой функции.

Для `\expandafter<sub>3</sub>` у нас есть

* $$\mathrm{T^3\_1 = T\_X}$$, который сохраняется для последующего *повторного вставления* обратно во ввод
* $$\mathrm{T^3\_2 = T\_Y}$$, который, как мы предположим, может быть развёрнут

Предположим также, что разворачивание $$\mathrm{T\_Y}$$ даёт следующую последовательность токенов: $$\mathrm{{T^1\_Y}{T^2\_Y}{T^3\_Y}}\cdots\mathrm{T^N\_Y}$$. Теперь мы достигли конца процесса разворачивания, начатого последовательностью команд `\expandafter<sub>1</sub>\expandafter<sub>2</sub>\expandafter<sub>3</sub>` $$\mathrm{T\_{X}T\_{Y}}$$ и TeX переходит к «раскручиванию» процесса рекурсии, начавшегося с `\expandafter<sub>1</sub>`.

После обработки `\expandafter<sub>3</sub>` у TeX в памяти есть список токенов, содержащий токены из разворачивания $$\mathrm{T\_Y\text{: }{T^1\_Y}{T^2\_Y}{T^3\_Y}}\cdots\mathrm{T^N\_Y}$$. Теперь TeX начинает повторно вставлять токены, которые он сохранил при обработке `\expandafter` команд:

1. TeX начинает с повторной вставки токена $$\mathrm{T\_X}$$ сохранённого `\expandafter<sub>3</sub>`. $$\mathrm{T\_X}$$ повторно вставляется *перед* разворачиванием $$\mathrm{T\_Y}$$, что приводит к последовательности токенов: $$\mathrm{{{T\_X}T^1\_Y}{T^2\_Y}{T^3\_Y}}\cdots\mathrm{T^N\_Y}$$.
2. Однако нам всё ещё нужно завершить процесс, начатый `\expandafter<sub>1</sub>` который сохранил токен, представляющий `\expandafter<sub>2</sub>`
3. Итоговая последовательность токенов, собранная TeX и готовая к чтению на следующем этапе обработки TeX, выглядит так: `\expandafter<sub>2 (token)</sub>` $$\mathrm{T\_{X}T^1\_{Y}T^2\_{Y}T^3\_{Y}\cdots T^N\_Y}$$
4. TeX теперь завершил «первый раунд» обработки и переходит к чтению созданной им последовательности списков токенов — эта последовательность начинается с `\expandafter<sub>2 (token)</sub>` который TeX затем обрабатывает. Для `\expandafter<sub>2</sub>` у нас есть
   * $$\mathrm{T^2\_1} =\ \mathrm{T\_X}$$ который сохраняется для последующего *повторного вставления* обратно во ввод
   * $$\mathrm{T^2\_2} = \mathrm{T^1\_Y}$$ который является первым токеном, возникающим при разворачивании $$\mathrm{T\_Y}$$; если он разворачиваемый, он разворачивается
5. Если мы предположим, что токен $$\mathrm{T^1\_Y}$$, первый токен из разворачивания $$\mathrm{T\_Y}$$, разворачивается в $$\mathrm{{T^A\_{Y1}}{T^B\_{Y1}}{T^C\_{Y1}}}$$ тогда, после того как TeX повторно вставил $$\mathrm{T\_X}$$, получившаяся последовательность токенов, которую TeX будет обрабатывать повторно, выглядела бы так: $$\mathrm{{T\_X}{T^A\_{Y1}}{T^B\_{Y1}}{T^C\_{Y1}}{T^2\_Y}{T^3\_Y}\cdots{T^N\_Y}}$$

   что мы можем переформулировать как

   $$\mathrm{T\_X}\text{\<expansion of the first token in }\mathrm{T\_Y}\text{>\<remaining tokens in }\mathrm{T\_Y}\text{>}$$

Следующая схема иллюстрирует списки токенов, создаваемые кодом TeX

```
    \expandafter1\expandafter2\expandafter3TXTY
```

![Изображение, показывающее обработку TeX нескольких команд \expandafter](/files/3fcada67b266a592f0f052f9264efc419109b527)

### От теории к практике

В качестве примера мы определим следующие макросы, которые будут служить $$\mathrm{T\_X}$$ и $$\mathrm{T\_Y}$$

* $$\mathrm{T\_X}=$$`\foo` где мы определяем `\foo` как `\def\foo#1{\textbf{#1}}`
* $$\mathrm{T\_Y}=$$`\bar` где сначала определяем `\def\abc{Hello}`, `\def\xyz{, World!}` и затем `\def\bar{\abc\xyz}`

Мы используем следующий фрагмент кода, чтобы продемонстрировать наш предыдущий анализ:

\expandafter\expandafter\expandafter\foo\bar

Из нашего обсуждения следует, что результат `\expandafter<sub>1</sub>\expandafter<sub>2</sub>\expandafter<sub>3</sub>` $$\mathrm{T\_X}\mathrm{T\_Y}$$ представляет собой последовательность токенов вида:

$$\mathrm{T\_X}\text{\<expansion of the first token in }\mathrm{T\_Y}\text{>\<remaining tokens in }\mathrm{T\_Y}\text{>}$$

где точная последовательность зависит от природы токена $$\mathrm{T\_Y}$$. Если мы подставим наши примерные команды, используя $$\mathrm{T\_X}=$$`\foo` и $$\mathrm{T\_Y}=$$`\bar`, который определён как `\def\bar{\abc\xyz}`, мы увидим:

* первый токен в $$\mathrm{T\_Y}$$ равно `\abc` а его разворачивание — это последовательность символьных токенов: `Hello`
* оставшиеся токены в $$\mathrm{T\_Y}$$ является единственным токеном, представляющим `\xyz`.

Если мы подставим эту информацию в наш «анализ», получим

$$\begin{align\*} &\mathrm{T\_X}\text{\<expansion of the first token in }\mathrm{T\_Y}\text{>\<remaining tokens in }\mathrm{T\_Y}\text{>}\ &=\text{foo}*{\text{token}}\text{\<expansion of \abc>}*\text{token list (characters)}\text{xyx}*\text{token}\ &=\text{foo}*\text{token}\text{Hello}*\text{token list (characters)}\text{xyx}*\text{token}\\\[10pt] \end{align\*}$$

Обратите внимание, что индексы <sub>токен</sub> и <sub>список токенов (символов)</sub> используются, чтобы подчеркнуть (напомнить нам), что TeX читает *целочисленные значения токенов*, а не *текстовые символы*, следовательно, нет необходимости показывать какие-либо пробельные символы или другие разделители после `\foo`: такие разделители уже давно обработаны или отброшены; здесь мы находимся в сугубо внутреннем мире TeX — мире списков токенов и целочисленных значений токенов.

Когда TeX обрабатывает последовательность списков токенов, созданную нашими `\expandafter` командами, он наберёт `**H**ello, World!`— только `H` выделяется полужирным. Мы могли бы добиться того же результата, написав эквивалентный код TeX `\foo Hello\xyz`. Обратите внимание, что определение `\foo` использовало единственный параметр; следовательно, `\foo` поглощает единственный `H` символьный токен в качестве своего аргумента, оставляя оставшиеся символьные токены (`ello`) нетронутыми.

**Примечания:**

* написав `\foo\bar` даёт совершенно другой вывод: токен для `\bar` использовался бы как аргумент для `\foo` что приводит к набору **`Hello, World`**— всё набирается полужирным.
* написав `\expandafter\foo\bar` приводит к тому, что `\bar` разворачивается, что даёт два токена: $$\text{abc}*\text{token}\text{xyz}*\text{token}$$. Затем, после того как $$\text{foo}*\text{token}$$ повторно вставляется `\expandafter` TeX обрабатывает последовательность токенов $$\text{foo}*\text{token}\text{abc}*\text{token}\text{xyz}*\text{token}$$ который набирает `**Hello**, World`— только **`Hello`** выделяется полужирным. Здесь единственный токен $$\text{abc}*\text{token}$$ обрабатывается как аргумент для токена макроса $$\text{foo}*\text{token}$$, оставляя токен $$\text{xyz}\_\text{token}$$ нетронутым, а его содержимое набирается текущим шрифтом.

## Примечание о \expandafter и макросах с аргументами

При использовании `\expandafter` чтобы принудительно развернуть макросы, стоит знать, как работает разворачивание макросов — особенно для макросов, принимающих аргументы. Прежде чем TeX сможет выполнить макрос — то есть прочитать и обработать токены, содержащиеся в определении макроса, — TeX должен «подготовить» макрос к выполнению, выполнив начальный процесс разворачивания макроса. Если определение макроса включает использование параметров (`#1`, `#2`, ... `#9`), то часть процесса разворачивания макроса требует, чтобы TeX просканировал ввод в поисках токенов, составляющих аргумент(ы), предоставленные пользователем: эти токены аргументов поглощаются (удаляются) из ввода. Во время разворачивания макроса TeX читает и поглощает токены из ввода, создавая мини-списки токенов — по одному списку на каждый аргумент; затем эти списки токенов будут вставлены в соответствующее место внутри тела макроса — когда TeX выполнит его. Последний шаг в разворачивании макроса включает нахождение TeX определения макроса, хранящегося в памяти, и организацию так, чтобы это место стало источником, из которого TeX будет читать следующий набор входных токенов. Выполнение макроса начинается, когда TeX начинает читать и обрабатывать эти токены, подавая на вход списки токенов, ранее созданные для хранения аргументов.

### Токены-разделители также поглощаются

Если исходное определение макроса также использовало токены, выступающие в роли разделителей, TeX также должен был бы сравнивать исходное определение макроса с использованием (вызовом) этого макроса пользователем, пытаясь найти и сопоставить токены-разделители. После того как они найдены/сопоставлены, токены-разделители впоследствии игнорируются, поскольку их единственная цель — служить «пунктуацией», помогая TeX выделить и определить реальные токены, предназначенные для формирования каждого аргумента.

&#x20;[Часть 1](/latex/ru/podrobnye-stati/19-how-does-expandafter-work-an-introduction-to-tex-tokens.md)   [Часть 2](/latex/ru/podrobnye-stati/22-how-does-expandafter-work-the-meaning-of-expansion.md)   [Часть 3](/latex/ru/podrobnye-stati/21-how-does-expandafter-work-tex-uses-temporary-token-lists.md)   [Часть 4](/latex/ru/podrobnye-stati/20-how-does-expandafter-work-from-basic-principles-to-exploring-tex-s-source-code.md)   [Часть 5](/latex/ru/podrobnye-stati/17-how-does-expandafter-work-a-detailed-macro-case-study.md)   [Часть 6](/latex/ru/podrobnye-stati/18-how-does-expandafter-work-a-detailed-study-of-consecutive-expandafter-commands.md)


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://overleaf-pro.ayaka.space/latex/ru/podrobnye-stati/18-how-does-expandafter-work-a-detailed-study-of-consecutive-expandafter-commands.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
