> For the complete documentation index, see [llms.txt](https://ayakaleaf-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://ayakaleaf-pro.ayaka.space/latex/ru/drugie-temy/22-how-tex-macros-actually-work-part-4.md).

# Как на самом деле работают макросы TeX: часть 4

[Часть 1](/latex/ru/drugie-temy/19-how-tex-macros-actually-work-part-1.md) [Часть 2](/latex/ru/drugie-temy/20-how-tex-macros-actually-work-part-2.md) [Часть 3](/latex/ru/drugie-temy/21-how-tex-macros-actually-work-part-3.md) [Часть 4](/latex/ru/drugie-temy/22-how-tex-macros-actually-work-part-4.md) [Часть 5](/latex/ru/drugie-temy/23-how-tex-macros-actually-work-part-5.md) [Часть 6](/latex/ru/drugie-temy/24-how-tex-macros-actually-work-part-6.md)

## Введение и обзор

В частях 1–3 мы подробно разобрали некоторые низкоуровневые сведения в качестве подготовки к пониманию того, как работают макросы TeX. В этой статье мы немного «всплывём на поверхность», чтобы пересмотреть некоторые основные практические принципы макросов TeX, готовясь к ещё одному глубокому погружению в частях 5 и 6. На протяжении всей статьи мы будем показывать примеры макросов, определённых с помощью примитивной команды TeX `\def`: мы не будем использовать, возможно, более знакомую, команду LaTeX `\newcommand`. На это есть очень веская причина: наша цель — понять фундаментальные принципы, лежащие в основе поведения макросов TeX, но для этого нам нужно использовать базовые команды *встроенных в* программного обеспечения TeX. Команды LaTeX, такие как `\newcommand`, сами являются макросами: командами с определённым запрограммированным поведением, которые в конечном счёте построены из слоёв более низкоуровневых примитивных команд TeX. Чтобы лучше понять фундаментальное поведение TeX, мы должны использовать примитивы TeX, а не макросы LaTeX.

### Куда мы идём?

По сути, мы идём к объяснению макросов как специализированной формы списка токенов: когда вы указываете TeX определить макрос, он создаёт последовательность токенов (список токенов) и хранит их в своей памяти, связывая с именем, которое вы задаёте. В частях 5 и 6 мы подробно рассмотрим списки токенов макросов, но если вы хотите перейти от этой статьи к чтению справочной информации, её можно найти в [Что такое список токенов TeX?](/latex/ru/podrobnye-stati/54-what-is-a-tex-token-list.md)

Списки токенов для макросов и их параметры включают несколько дополнительных тонкостей, которые мы подробно разберём — и воспользуемся множеством примеров. Чтобы лучше понять поведение TeX при обработке макросов, нужно помнить по сути один ключевой факт: TeX думает о символах только на самом раннем этапе обработки входных данных; с этого момента и далее — только токены! В этой статье и в остальных статьях этой серии мы исследуем роль, которую токены играют в макросах TeX.

## Четыре части макроса

### Напоминание: параметры макроса и аргументы макроса

Прежде чем начать, стоит напомнить себе о разнице между *параметрами* и *аргументы*, потому что в этой статье мы будем использовать оба термина. Допустим, вы определяете макрос \foo

```
\def\foo#1#2{This is #1, this is #2}
```

Конструкции (токены) `#1`, `#2` (до `#9`) называются *параметрами*: считайте их «заполнителями» для фактических данных, которые вы будете использовать при вызове макроса:

```
\foo{alpha}{beta}
```

Здесь `alpha` и `beta` являются *аргументы* используемыми при этом вызове \foo: аргументы макроса — это фактические значения, подставляемые в заполнители параметров (`#1`, `#2`... `#9`) которые использовались при определении макроса.

### Определение макроса: состоит из 4 элементов

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

Любое определение макроса состоит из 4 частей:

```
<примитив макроса TeX><имя макроса><текст параметров>{<текст замены>}
```

где:

* `<примитив макроса TeX>`: один из `\def`, `\edef`, `\gdef` или `\xdef`;
* `<имя макроса>`: имя, данное вашей команде-макросу, например `\foo`;
* `<текст параметров>`: это может отсутствовать, но если оно присутствует, то это серия *токены* которые стоят перед `<текст замены>` макроса.  `<текст параметров>` может содержать параметры макроса (`#1`, `#2`... `#9`) и другие типы токенов. По сути, и как мы увидим подробно,  `<текст параметров>` предоставляет своего рода *шаблон токенов* который TeX использует, чтобы определить, какие токены пользователь хочет использовать в качестве аргументов макроса: когда вы вызываете макрос, TeX передаёт *аргумента* токены в ваш `<текст замены>` (который также является списком токенов);
* `{<текст замены>}`: это фактическое тело вашего макроса: это последовательность токенов, в которую *аргументы* «вставляются» аргументы при обработке (раскрытии) макроса. Аргументы подставляются в позиции, обозначенные параметрами макроса, использованными в исходном определении.

**ПРИМЕЧАНИЕ**: На протяжении всего обсуждения мы предполагаем, что `<имя макроса>` за ним будет следовать пробел с кодом категории 10, который будет служить разделителем для завершения `<имя макроса>`. Мы *не* показали этот символ пробела явно в нашем тексте/обсуждении, но предполагаем, что он там есть. Строго говоря, его следовало бы представить примерно так:

```
<TeX macro primitive><macro name><space><parameter text>{<replacement text>}
```

Однако мы опустим явное включение `<space>` символа и будем неявно предполагать его наличие.

#### Заметки о {}

Мы показали использование двух фигурных скобок: `{` и `}` которые окружают (ограничивают) фактическое тело вашего `<текст замены>`. `{` и `}` — это всего лишь принятая конвенция, потому что на самом деле TeX ожидает, что текст вашего макроса `<текст замены>` будет начинаться символом с кодом категории 1 («Начало группы») и заканчиваться символом с кодом категории 2 («Конец группы»). По соглашению, это `{` и `}` соответственно символы. Однако при желании вы можете назначить для этого любую пару символов. Например

```
\catcode`\(=1
\catcode`\)=2
```

Теперь вы можете определять и использовать макросы так:

```
\def\foo #1(Hello, #1)
\foo(World!)
```

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

```
\def\foo #1{Hello, #1}
\foo{World!}
```

### и {}

Компоненты определения макроса, наиболее важные для нашего обсуждения, — это `<текст параметров>` и `{<текст замены>}`. Когда вы определяете макрос, `<текст параметров>` по сути, является строгим шаблоном токенов, который определяет, как следует использовать макрос. Как было отмечено, `<текст параметров>` может быть пустым, например, `\def\foo{Some text}` где между именем нашей команды (`\foo`) и открывающей фигурной скобкой `{` которая здесь обозначает начало `<текст замены>`.

**Примечание**: Некоторые читатели, возможно, знают о механизме TeX «hashquote» (`#{`) но мы не будем здесь этого касаться.

### и разделители макроса

Эта конкретная статья не задумана как исчерпывающе полное руководство по написанию макросов, но стоит сделать короткое повторение с примерами, показывающее, что `<текст параметров>` может стать сложным, потому что TeX позволяет `<текст параметров>` содержать:

* **параметры макроса**: (`#1`, `#2`,... `#9`) которые служат «заполнителями» для значений, которые пользователь предоставит при запуске макроса, — *аргументы*;
* **токены-разделители**: произвольные токены, вставленные между/вокруг токенов параметров и используемые для задания границы между параметрами макроса. Можно считать, что эти разделители образуют своего рода «пунктуацию», которая делает `<текст параметров>` «шаблон токенов», которому вам нужно следовать при использовании макроса. Токены-разделители не выводятся в набор.

Так что же это на самом деле означает — давайте посмотрим на несколько примеров.  `<текст параметров>` находится между именем макроса и левой фигурной скобкой `{` определения макроса.

#### Разделители: пример 1

Предположим, мы определим простой макрос `\foo` следующим образом:

```
\def\foo ABC{hello, there}
```

* `<текст параметров>` = `ABC`
* Здесь нет токенов параметров (`#1`, `#2`,... `#9`)
* Разделителями являются *три символьных токена* `ABC` но в этом примере они несколько избыточны и используются лишь в качестве примера.

Три символьных токена `ABC` рассматриваются как разделители: это не то, что выводится в набор, а «пунктуация», которая должна присутствовать при вызове макроса — когда вы используете макрос `\foo` вы должны указать те же разделители, которые присутствовали при его определении.

Если вы введёте `\foo ABC` внутри текста, это выведет `hello, there`— символьные токены `ABC` не выводятся в набор, но TeX очень тщательно проверил, чтобы убедиться, что они присутствуют в вашем вызове `\foo`. TeX ищет эти разделители («пунктуацию») и удаляет (поглощает) их, как вы можете видеть на следующем скриншоте Overleaf: `ABC` не выводится в набор:

![Overleaf выполняет макрос TeX](/files/9fc344d9c28a7f9ddcc8d7926cc4bf589a23bab8)

Если вы попытаетесь использовать `\foo` без `ABC` разделителей TeX выдаст ошибку `Использование \foo не соответствует его определению.`:

![Показ ошибки TeX в Overleaf](/files/5046b4dfef463db446d8d512d6e5053b4dca4dba)

В приведённом выше примере, после обнаружения `\foo` TeX ожидает увидеть три символьных токена `ABC` но не видит их: он видит символьный токен для `w` и немедленно обнаруживает, что что-то не так.

#### Разделители: пример 2

Интересный и, возможно, неожиданный факт: разделителями могут быть произвольные токены-команды — включая команды, которые *даже не были определены*. Например, мы могли бы определить `\foo` как:

```
\def\foo A\bob B\anne{Hello \TeX{}}
```

* `<текст параметров>` = `A\bob B\anne`
* Здесь нет токенов параметров (`#1`, `#2`,... `#9`)
* Разделителями являются токены `A\bob B\anne` но опять же в этом примере они избыточны и нужны только для обсуждения. Обратите внимание, что `\bob` и `\anne` — это вымышленные команды, которые *не были определены*— и не должны быть.

Когда вы вызываете макрос, TeX проверяет (сканирует) `<текст параметров>` что присутствует *в вашем вызове макроса* и сравнивает его токен за токеном с версией «шаблона токенов» *хранящейся в памяти*— той, которая была создана в момент определения макроса. TeX сканирует *использованные в вашем вызове макроса* и просто преобразует любые найденные там команды в их числовое значение токена: он не пытается выполнять эти команды, поэтому не имеет значения, что `\bob` и `\anne` никогда не были определены. TeX просто использует хранящийся в памяти шаблон токенов как ориентир, который позволяет ему определить, какие токены в `<текст параметров>` вашего вызова макроса являются фактическими аргументами, которые нужно подать в `<текст замены>`.

Как вы можете видеть из следующего фрагмента экрана Overleaf, ни один из символьных токенов (`Один` и `B`) внутри `A\bob B\anne` не был выведен в набор, и наши неопределённые команды `\bob` и `\anne` не вызвали никаких проблем. Все эти токены были *поглощены* TeX при сопоставлении вашего использования `\foo` с определением (шаблоном токенов) `\foo` (списком токенов), хранящимся в памяти.

![Overleaf выполняет макрос TeX](/files/09e18e9fa5e0cafdfe9d3114a24a7685ffd09a2d)

#### Разделители: пример 3

Вы можете перемежать различные разделители (символьные токены, токены-команды) с параметрами макроса, как в этом примере:

```
\def\foo A\bob#1B\anne#2\jane#3bye!{Hello from \TeX{} to \#1=#1, \#2=#2 and \#3=#3}
```

* `<текст параметров>` = `A\bob#1B\anne#2\jane#3bye!`
* Здесь 3 токена параметров макроса: `#1`, `#2`, `#3`
* На этот раз параметры (`#1`, `#2`, `#3`) ограничены комбинацией токенов:

  `A\bob#1B\anne#2\jane#3bye!`

Здесь вы, по сути, предоставили TeX шаблон, который он будет очень тщательно пытаться и ожидать сопоставить, когда вы вызываете `\foo`: он пройдёт по вашему вызову макроса, *токен за токеном*, и будет ожидать сопоставления (нахождения):

* двух токенов `Один` и `\bob` до `#1`
* двух токенов `B` и `\anne` до `#2`
* токена `\jane` перед параметром `#3` и *четырёх символьных токенов* `b`, `y`,`e` и `!` после `#3`.

**Помните**: TeX мыслит токенами, поэтому `bye!` равно *четырёх символьных токенов*.

Мы можем использовать этот макрос двумя способами — мы могли бы заключить любые много-токенные аргументы в фигурные скобки, чтобы образовать группу:

```
\foo A\bob{This}B\anne{That}\jane{Other}bye!
```

Однако это *не обязательно* потому что в определении нашего макроса есть разделители, которые задают шаблон токенов. TeX может использовать этот шаблон, чтобы выделить токены для каждого аргумента среди токенов, которые являются лишь разделителями. Мы можем использовать наш макрос так:

```
\foo A\bob ThisB\anne That\jane Otherbye!
```

и TeX может выделить аргументы, чтобы получить тот же результат, что и при использовании групп `{...}`:

![Overleaf выполняет макрос TeX с использованием разделителей](/files/20013f921f8d131d163768eb54edf3aa92568d62)

Обратите внимание, как TeX смог точно определить, какие токены соответствовали параметрам `#1` и `#3`:

![Overleaf выполняет макрос TeX с использованием разделителей](/files/b25005834824e05dbbc0bd425f25fdd6998c585e)

#### Разделители: пример 4 (речь идёт о токенах, а не о символах!)

Вот короткий пример, демонстрирующий, что при работе с макросами важно помнить: мы действительно находимся в мире *токены* и **не** *символы*…

Мы определим следующий короткий макрос, где A и B — символы, выступающие в роли разделителей, и оба имеют код категории 11. Это работает без проблем:

```
\documentclass{article}
\begin{document}
\def\foo A#1B{Hello, #1}
\foo AGrahamB
\end{document}
```

и выводит `Hello, Graham`.

Однако если вы измените код категории A или B на что-то отличное от 11, вызов макроса завершится неудачей. Предположим, мы изменим код категории B на 12, используя ``\catcode`B=12`` и снова попробуем вызвать макрос, как и прежде:

```
\documentclass{article}
\begin{document}
\def\foo A#1B{Hello, #1}
\foo AGrahamB % This works
\catcode`\B=12\relax
\foo AGrahamB
\end{document}
```

Он завершается с несколько озадачивающей ошибкой:

```
Сбежавший аргумент?
GrahamB \end {document}
! Файл закончился во время сканирования использования \foo.
<вставленный текст>
                \par
<*> main.tex

Я подозреваю, что вы забыли `}', из-за чего я
прочитал дальше места, где вы хотели, чтобы я остановился.
Я попытаюсь восстановиться; но если ошибка серьёзная,
вам лучше сейчас ввести `E' или `X' и исправить файл.

! Аварийная остановка.
<*> main.tex

*** (задача прервана, допустимый \end не найден)
```

![Overleaf показывает ошибку макроса TeX](/files/be52a8ddbb585c11b1d6532d63d4468ccfac3945)

К сожалению, стандартное предположение TeX о том, что ``Я подозреваю, что вы забыли `}'`` равно **неверно**, оно вызвано не отсутствующей фигурной скобкой (`}`).

#### Что произошло?

TeX пытается сопоставить `\foo` макрокоманду *вызов* с `\foo` макрокоманду *определением* которое он хранит в памяти. Когда вы вызываете макрос, которому нужны один или несколько аргументов, TeX должен изучить (просканировать) ваше использование макроса, чтобы определить *аргументы* передаваемые вами этому макросу. Здесь TeX ожидает, что аргумент будет заключён между A (код категории 11) и B (код категории 11). Помните: TeX мыслит *токены*, **не** *символы*.

Значения токенов для A и B, $$\mathrm{T\_A}$$ и $$\mathrm{T\_B}$$ соответственно, равны:

$$\mathrm{T\_A = 256 \times 11 + 65 = 2881}$$ $$\mathrm{T\_B = 256 \times 11 + 66 =2882}$$

Однако по мере продолжения поиска TeX видит B *но* теперь он имеет код категории 12, и это даёт другое значение токена:

$$\mathrm{T'\_B= 256 \times 12 + 66 =3138}$$

В `\foo` определении макроса значение токена для B (используемого как разделитель) было 2882, но TeX теперь видит значение токена 3138: он думает, что это просто ещё один токен, предназначенный для использования в *аргумента* переданном в `\foo`С точки зрения TeX, последний токен аргумента ещё не найден, поэтому он берёт следующий токен в поисках B с кодом категории 11. Именно тогда и поэтому макрос терпит неудачу: в попытке найти аргумент TeX «переразгоняется» и начинает читать токены, которые вы не собирались включать в аргумент для `\foo` вызова макроса. Что произойдёт дальше, зависит от токенов, которые TeX обнаружит после B — они вызовут различные `Сбежавший аргумент?` ошибки.

В нашем примере TeX «переразгоняется» и поглощает поток токенов `\end {document}` и быстро достигает конца файла, отсюда сообщение об ошибке:

```
! Файл закончился во время сканирования использования \foo
```

## Часть 5

Как мы видели выше, `<текст параметров>` часть определения нашего макроса может варьироваться от чрезвычайно простой до сложной смеси параметров макроса, перемежающихся символьными токенами и токенами-командами, выступающими в роли разделителей. TeX способен обрабатывать различные комбинации токенов, присутствующие в `<текст параметров>` чтобы извлекать аргументы, переданные макросу, и обнаруживать, когда мы пытаемся использовать макрос неправильно. Как он это делает, — тема следующей статьи серии.

[Часть 1](/latex/ru/drugie-temy/19-how-tex-macros-actually-work-part-1.md) [Часть 2](/latex/ru/drugie-temy/20-how-tex-macros-actually-work-part-2.md) [Часть 3](/latex/ru/drugie-temy/21-how-tex-macros-actually-work-part-3.md) [Часть 4](/latex/ru/drugie-temy/22-how-tex-macros-actually-work-part-4.md) [Часть 5](/latex/ru/drugie-temy/23-how-tex-macros-actually-work-part-5.md) [Часть 6](/latex/ru/drugie-temy/24-how-tex-macros-actually-work-part-6.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://ayakaleaf-pro.ayaka.space/latex/ru/drugie-temy/22-how-tex-macros-actually-work-part-4.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.
