> 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/17-how-does-expandafter-work-a-detailed-macro-case-study.md).

# Как работает \\\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 из The $$\varepsilon\mathrm{\text{-}{\TeX}}$$ Руководство

Это $$\varepsilon\mathrm{\text{-}{\TeX}}$$ механизм верстки был создан на основе программы TeX Кнута и изначально задумывался как «промежуточный» этап на пути к разработке [Новой системы верстки](https://en.wikipedia.org/wiki/New_Typesetting_System) (NTS), написанной на языке программирования Java. $$\varepsilon\mathrm{\text{-}{\TeX}}$$ впервые была разработана в конце 1990-х, чтобы добавить набор новых примитивных команд, предоставляющих дополнительную функциональность, недоступную в оригинальной программе Кнута. Хотя $$\varepsilon\mathrm{\text{-}{\TeX}}$$ с момента своего первоначального выпуска периодически обновлялся, сегодня он не получил широкого распространения как самостоятельный механизм верстки, хотя его нововведения были переняты позднейшими поколениями TeX: pdfTeX, XeTeX и LuaTeX.

Это [$$\varepsilon\mathrm{\text{-}{\TeX}}$$ manual](http://mirror.ox.ac.uk/sites/ctan.org/systems/doc/etex/etex_man.pdf) содержит поучительный пример макроса, который умело использует `\expandafter`:

```
    \def\foo#1#2{\number#1
    \ifnum#1<#2,
    \expandafter\foo
    \expandafter{\number\numexpr#1+1\expandafter}%
    \expandafter{\number#2\expandafter}%
    \fi}
```

`\foo` реализует механизм цикла таким образом, что `\foo{7}{13}` дает `7, 8, 9, 10, 11, 12, 13`; однако, `\foo` не использует никаких *присваиваний переменным* для управления процессом цикла — что делает его интересным макросом для подробного рассмотрения.

### Немного предыстории: выражения и присваивания

Важный элемент `\foo`кода — это использование команды `\numexpr`, команды из набора из четырех связанных примитивов, впервые введенных $$\varepsilon\mathrm{\text{-}{\TeX}}$$: `\numexpr`, `\dimexpr`, `\glueexpr` и `\muexpr`. Их назначение — строить так называемые *выражения* которые позволяют вычислять/обрабатывать значения TeX типа number, dimen, glue или muglue (соответственно). Как обсуждается на страницах 8–9 The $$\varepsilon\mathrm{\text{-}{\TeX}}$$ Руководства, важной особенностью *выражения* является то, что их вычисление (расчет) не требует от TeX выполнять какие-либо *присваивания*.

В терминах программирования присваивание — это процесс установки (присвоения) переменной определенного значения; например, присваивание `\count` регистру `99` содержать значение `12345` через `\count99=12345`. Во время обработки TeX происходит множество других видов присваиваний — например, присваивание токен-реестрам содержать последовательность токенов, присваивание блочным реестрам содержать содержимое блока и так далее.

Чтобы выполнить присваивание, например `\count99=12345`, TeX должен задействовать (выполнить) внутренний код, реализующий поведение `\count` или любого другого примитива, выполняющего какой-либо вид присваивания. Однако бывают моменты, когда TeX выполняет чистое *расширения* и в такие моменты такие присваивания не выполняются —*в этот момент обработки TeX*. Примеры такой ситуации включают следующие команды:

* `\edef\command {*token list*}` команда определения макроса «расширенное определение», которая расширяет токены в *списке токенов* и сохраняет результат как определение `\command`.
* `\write *number* {*token list*}` расширяет токены в `*token list*` и записывает их в файл, обозначаемый `*number*`.
* `\directlua {*token list*}` эта примитивная команда LuaTeX используется для передачи кода Lua встроенному интерпретатору Lua. Все токены в `*token list*` полностью раскрываются перед передачей интерпретатору Lua для выполнения.

#### Быстрый пример \edef

Если мы запишем следующие базовые макросы:

```
     \def\mycount{\count99=12345}
     \edef\mymacro{\mycount}
```

`\edef` будет раскрывать `\mycount` в его составляющие токены, но не пойдет дальше: ни одна из команд, содержащихся в определении `\mymacro` не будут выполнены: то есть присваивание `12345` на `\count99` *в этот момент не происходит*; только когда мы вызываем `\mymacro` это присваивание произойдет, когда TeX выполнит код для обработки `\count` примитива. Когда TeX выполняет *действия, связанные только с расширением* любые присваивания будут выполнены позже в процессе обработки TeX, а не во время самого процесса расширения.

#### Почему присваивания здесь представляют интерес?

При написании кода для выполнения цикла — на любом языке программирования — обычно выделяют переменную, которая служит «счетчиком цикла»: она используется для управления числом выполнений цикла. Обычно цикл контролируется проверкой того, достигла ли эта переменная-счетчик определенного значения — эта переменная увеличивается (или уменьшается) на каждой итерации цикла. Однако изменение переменной-счетчика означает присвоение ей нового значения, что в TeX обычно требует примитивной команды `\advance` для увеличения (или уменьшения) значения, хранящегося в `\count` регистре. Как мы уже видели, во время чистого процесса расширения TeX такие присваивания (включая увеличение переменных) не могут происходить: макрос `\foo` ловко обходит это ограничение.

### Вернемся к объяснению \foo

Макрос `\foo` способен управлять процессом цикла *без* без необходимости присваивать значения каким-либо переменным: он контролирует, как часто происходит цикл, используя данные, возникающие в результате расширения: значения данных, хранящиеся во временных списках токенов. Используя наши знания об использовании TeX (создании) временных списков токенов, мы можем внимательнее посмотреть, чтобы точно понять, как `\foo` достигает своих результатов.

**Помните**: Мы разбираем выполнение макроса после того, как исходный текст его определения — содержащийся в физическом `.tex` файле — был просканирован (прочитан TeX) и преобразован в список токенов, представляющий определение макроса. По сути, мы следим за обработкой TeX этих сохраненных *токены* пока он читает и обрабатывает токены в определении макроса, находящемся где-то в памяти TeX. Любые пробельные символы, изначально присутствовавшие в коде TeX определения макроса (текст внутри `.tex` файла), будут поглощены, когда TeX просматривал этот текст в поисках команд (пробелы как разделители), либо будут преобразованы в токены, например пробел после запятой (`,`) в `\ifnum#1<#2,` который возник в результате преобразования символа конца строки (`\r`) в пробел.

Поскольку код TeX в `\foo` использует несколько `\expandafter` команд, мы упростим объяснение, добавив нижние индексы к каждому `\expandafter`, указывая, к какому именно мы обращаемся. Кроме того, мы расширим обозначения для токенов, обрабатываемых `\expandafter` на $$\mathrm{T^i\_1}$$ и $$\mathrm{T^i\_2}$$, обозначая токены $$\mathrm{T\_1}$$ и $$\mathrm{T\_2}$$ для `\expandafter<sub>i</sub>`: `\expandafter<sub>i</sub>` $$\mathrm{T^i\_1T^i\_2}$$

Вот аннотированный код макроса:

```
    \def\foo#1#2{\number#1
    \ifnum#1<#2,
    \expandafter1\foo
    \expandafter2{\number\numexpr#1+1\expandafter3}%
    \expandafter4{\number#2\expandafter5}%
    \fi}
```

`\foo` начинается с `\number#1` который использует расширяемую команду `\number` для преобразования значения первого аргумента в его типографское представление. Команда `\number` работает, создавая временный список токенов, содержащий символьные токены, которые представляют отдельные цифры, входящие в числовое значение, над которым `\number` она работает. Этот список токенов становится следующим источником ввода TeX. Здесь этот список токенов читается, и токены выводятся, чтобы набрать значение `#1`.

Затем макрос выполняет проверку `\ifnum#1<#2` чтобы проверить, меньше ли аргумент `#1` меньше, чем аргумент, переданный для `#2`. Если это так, запятая (`,`) выводится (набирается), за которым следует некоторый пробел, возникший из токена, сгенерированного из символа перевода строки после запятой (`,`). Этот пробельный символ впервые был сгенерирован, когда TeX прочитал эту строку из `.tex` файла.

Макрос продолжает обработку следующего фрагмента кода, который является ядром его работы:

```
    \expandafter1\foo
    \expandafter2{\number\numexpr#1+1\expandafter3}%
    \expandafter4{\number#2\expandafter5}%
    \fi}
```

По сути, этот код генерирует серию временных списков токенов, которые приводят к многократным вызовам `\foo` макроса, прекращающимся, когда if-проверка `\ifnum#1<#2` больше не истинна. Но *как* как здесь управляется цикл, если никаких присваиваний не происходит: где же «счетчик цикла»?

Начнем с рассмотрения кода `\expandafter<sub>1</sub>\foo\expandafter<sub>2</sub>`. Обратите внимание, что мы будем использовать запись с нижними индексами `<sub>token</sub>` (или `<sub>(token)</sub>`) чтобы напомнить себе, что здесь TeX читает/обрабатывает числовые (целочисленные) значения токенов.

Здесь в качестве входных данных для `\expandafter<sub>1</sub>`:

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

Для `\expandafter<sub>2</sub>` получаем:

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

**Примечание:**`\number` — это расширяемая команда, предназначенная для «преобразования в токены»: то есть преобразования числовой величины в последовательность символьных токенов, которые представляют эту величину. Когда `\number` раскрывается, первое, что делает TeX, — просматривает входные данные в поисках целых чисел: процесс, который запускает дальнейшее расширение.

**Ключ к разгадке:** Здесь `\number` действует на *выражение* `\numexpr#1+1` которое вычисляет значение `#1+1`. Результат этого вычисления обрабатывается `\number` для преобразования его во временный список токенов, содержащий символьные токены, представляющие значение `#1 + 1`. Этот временный список токенов, сгенерированный `\number`, в конечном итоге будет прочитан как первый аргумент другого вызова `\foo`. Вместо увеличения счетчика цикла (через `\advance` и присваивание), использование `\numexpr` создает новое значение без необходимости присваивания. Благодаря этому механизму переменная, управляющая циклом (`\foo`параметр `#1`) увеличивается, и итерация цикла контролируется и завершается: весьма изящно!

Затем `\expandafter<sub>3</sub>` обрабатывается, в результате получаем:

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

Для `\expandafter<sub>4</sub>` получаем:

* $$\mathrm{T^4\_1} =$$`{<sub>token</sub>` который сохраняется для последующего *повторного вставления* обратно во ввод
* $$\mathrm{T^4\_2} =$$`\number<sub>token</sub>` который раскрывается и преобразует `#2` в еще один временный список токенов.

Наконец,`\expandafter<sub>5</sub>` раскрывается:

* $$\mathrm{T^5\_1} =$$`}<sub>token</sub>` который сохраняется для последующего *повторного вставления* обратно во ввод
* $$\mathrm{T^5\_2} =$$`\fi<sub>token</sub>`, которая является расширяемой командой.

  Раскрытие `\fi` фактически завершает `\ifnum` и, по сути, закрывает эту итерацию макроса. Теперь TeX завершает повторную вставку всех токенов, временно сохраненных множественными `\expandafter` командами: это генерирует серию списков токенов из одного токена, возникающих из токенов, сохраненных каждым `\expandafter`. Кроме того, TeX также создал списки токенов из-за действия `\number`.

### Сборка списков токенов

По сути, макрос `\foo` генерирует последовательность списков токенов: можно считать `\foo` это «фабрикой» по производству списков токенов. Эти списки токенов читаются TeX и становятся следующими источниками ввода. Хитрость заключена в одном из более ранних действий `\foo`:

```
    \expandafter1\foo\expandafter2
```

через которое `\foo` организует повторный вызов самого себя, но с другими аргументами, которые хранятся в списках токенов, построенных `\number`. Чтобы эти списки токенов в совокупности вели себя как вызов макроса, фигурные скобки `{` и `}` были сохранены и повторно вставлены во входные данные (как списки из одного токена) благодаря действиям `\expandafter` команд.

![списки токенов, сгенерированные макросом \foo](/files/e82da09287896b288138043d8ac7364f2ff57ce7)

&#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/17-how-does-expandafter-work-a-detailed-macro-case-study.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.
