Это многостраничный печатный вид этого раздела. Нажмите что бы печатать.

Вернуться к обычному просмотру страницы.

Latex для авторов

LATEX для авторов актуальная версия

Введение

LaTeX 2ε был выпущен в 1994 году и добавил ряд новых концепций, которые на тот момент были актуальны. Эти концепции описаны в документе usrguide-historic, который в значительной степени остался неизменным. С тех пор команда LaTeX работала над рядом идей, в первую очередь над языком программирования для LaTeX (expl3), а затем над рядом инструментов для авторов документов, которые основываются на этом языке. Здесь мы описываем стабильные и широко используемые концепции, которые стали результатом этой работы. Эти «новые» идеи были перенесены из пакетов разработки в ядро LaTeX 2ε. Таким образом, они теперь доступны всем пользователям LaTeX и имеют такую же стабильность, как и любая другая часть ядра. Тот факт, что «за кулисами» они построены на основе expl3, полезен для команды разработчиков, но не имеет прямого значения для пользователей.

2 Создание команд и окружений документа

2.1 Обзор

Создание команд и окружений документа с использованием инструментария LaTeX3 основано на идее, что общий набор описаний может быть использован для охвата почти всех типов аргументов, используемых в реальных документах. Таким образом, парсинг сводится к простому описанию того, какие аргументы принимает команда: это описание обеспечивает «связь» между синтаксисом документа и реализацией команды.

Сначала мы опишем типы аргументов, затем перейдем к объяснению того, как их можно использовать для создания как команд документа, так и окружений. Затем будут описаны различные более специализированные функции, которые позволяют еще более богатое применение простого интерфейса.

Детали, приведенные здесь, предназначены для помощи пользователям в создании команд документа в общем. Более технические детали, подходящие для программистов TeX, включены в раздел interface3.

2.2 Описание типов аргументов

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

Основная форма спецификатора аргумента — это список букв, где каждая буква определяет тип аргумента. Как будет описано ниже, некоторые типы требуют дополнительной информации, такой как значения по умолчанию. Типы аргументов можно разделить на две категории: те, которые определяют обязательные аргументы (возможно, вызывая ошибку, если они не найдены), и те, которые определяют необязательные аргументы.

Обязательные типы

  • m
    Стандартный обязательный аргумент, который может быть либо единственным токеном, либо несколькими токенами, заключенными в фигурные скобки {}. Независимо от ввода, аргумент будет передан во внутренний код без внешних скобок. Это спецификатор типа для обычного аргумента TeX.

  • r
    Указывается как r⟨token1⟩⟨token2⟩, это обозначает «обязательный» аргумент с разделителями, где разделителями являются ⟨token1⟩ и ⟨token2⟩. Если открывающий разделитель ⟨token1⟩ отсутствует, будет вставлен маркер по умолчанию -NoValue- после соответствующей ошибки.

  • R
    Указывается как R⟨token1⟩⟨token2⟩{⟨default⟩}, это «обязательный» аргумент с разделителями, как и r, но он имеет определяемое пользователем значение по умолчанию ⟨default⟩ вместо -NoValue-.

  • v
    Читает аргумент «дословно» между следующим символом и его следующим вхождением, аналогично аргументу команды LaTeX 2ε \verb. Таким образом, аргумент типа v читается между двумя одинаковыми символами, которые не могут быть %, , #, {, } или ␣. Дословный аргумент также может быть заключен в фигурные скобки { и }. Команда с дословным аргументом вызовет ошибку, если она появится внутри аргумента другой команды.

  • b
    Подходит только в спецификации аргумента окружения, обозначает тело окружения, между \begin{⟨environment⟩} и \end{⟨environment⟩}. См. раздел 2.11 для подробностей.

Типы, которые определяют необязательные аргументы, следующие:

  • o
    Стандартный необязательный аргумент LaTeX, заключенный в квадратные скобки, который будет предоставлять специальный маркер -NoValue-, если не указан (как будет описано позже).

  • d
    Указывается как d⟨token1⟩⟨token2⟩, необязательный аргумент, который ограничен ⟨token1⟩ и ⟨token2⟩. Как и в случае с o, если значение не указано, возвращается специальный маркер -NoValue-.

  • O
    Указывается как O{⟨default⟩}, аналогично o, но возвращает ⟨default⟩, если значение не указано.

  • D
    Указывается как D⟨token1⟩⟨token2⟩{⟨default⟩}, аналогично d, но возвращает ⟨default⟩, если значение не указано. Внутренне типы o, d и O являются сокращениями для соответствующим образом сконструированного аргумента типа D.

  • s
    Необязательная звезда(*), которая приведет к значению \BooleanTrue, если звезда присутствует, и \BooleanFalse в противном случае (как будет описано позже).

  • t
    Необязательный ⟨token⟩, который приведет к значению \BooleanTrue, если ⟨token⟩ присутствует, и \BooleanFalse в противном случае. Указывается как t⟨token⟩.

  • e
    Указывается как e{⟨tokens⟩}, набор необязательных украшений, каждое из которых требует значения. Если украшение отсутствует, возвращается -NoValue-. Каждое украшение дает один аргумент, упорядоченный в соответствии со списком ⟨tokens⟩ в спецификации аргумента. Все ⟨tokens⟩ должны быть различными.

  • E
    Аналогично e, но возвращает одно или несколько ⟨defaults⟩, если значения не указаны: E{⟨tokens⟩}{⟨defaults⟩}. См. раздел 2.7 для получения дополнительных деталей.

2.3 Модификация описаний аргументов

В дополнение к типам аргументов, обсужденным выше, описание аргументов также придает специальное значение трем другим символам.

Во-первых, + используется для того, чтобы сделать аргумент длинным (принимать параграфные токены). В отличие от \newcommand, это применяется на основе каждого отдельного аргумента. Таким образом, модификация примера на «s o o +m O{default}» означает, что обязательный аргумент теперь является \long, в то время как необязательные аргументы таковыми не являются.

Во-вторых, ! используется для управления тем, разрешены ли пробелы перед необязательными аргументами. Здесь есть некоторые тонкости, так как сам TeX имеет ограничения на то, где пробелы могут быть «обнаружены»: более подробная информация приведена в разделе 2.6.

В-третьих, = используется для объявления того, что следующий аргумент должен интерпретироваться как серия ключевых значений (keyvals). См. раздел 2.9 для получения дополнительных деталей.

Наконец, символ > используется для объявления так называемых «процессоров аргументов», которые могут быть использованы для модификации содержимого аргумента перед его передачей в определение макроса. Использование процессоров аргументов является несколько продвинутой темой (или, по крайней мере, менее часто используемой функцией) и рассматривается в разделе 2.10.

Вот перевод раздела 2.4 “Создание команд и окружений документа”:


2.4 Создание команд и окружений документа

\NewDocumentCommand {⟨cmd⟩} {⟨arg spec⟩} {⟨code⟩}
\RenewDocumentCommand {⟨cmd⟩} {⟨arg spec⟩} {⟨code⟩}
\ProvideDocumentCommand {⟨cmd⟩} {⟨arg spec⟩} {⟨code⟩}
\DeclareDocumentCommand {⟨cmd⟩} {⟨arg spec⟩} {⟨code⟩}

Эта группа команд используется для создания команды ⟨cmd⟩. Спецификация аргумента для функции задается с помощью ⟨arg spec⟩, а команда использует ⟨code⟩, где #1, #2 и т. д. заменяются на аргументы, найденные парсером.

Пример:

\NewDocumentCommand\chapter{s o m} {
  \IfBooleanTF{#1}
    {\typesetstarchapter{#3}}
    {\typesetnormalchapter{#2}{#3}}
}

Это способ определения команды \chapter, которая будет вести себя аналогично текущей команде LaTeX 2ε (за исключением того, что она будет принимать необязательный аргумент, даже когда был распарсен символ *). Команда \typesetnormalchapter может проверить свой первый аргумент на наличие значения -NoValue-, чтобы определить, присутствует ли необязательный аргумент. (См. раздел 2.8 для деталей о \IfBooleanTF и проверке на -NoValue-.)

Разница между версиями \New…, \Renew…, \Provide… и \Declare… заключается в поведении, если ⟨cmd⟩ уже определена.

  • \NewDocumentCommand выдаст ошибку, если ⟨cmd⟩ уже была определена.
  • \RenewDocumentCommand выдаст ошибку, если ⟨cmd⟩ ранее не была определена.
  • \ProvideDocumentCommand создаст новое определение для ⟨cmd⟩ только в том случае, если оно еще не было дано.
  • \DeclareDocumentCommand всегда создаст новое определение, независимо от существующей ⟨cmd⟩ с тем же именем. Это следует использовать с осторожностью.

Если ⟨cmd⟩ не может быть предоставлена как единый токен, но требует «конструирования», вы можете использовать \ExpandArgs, как объясняется в разделе 4, который также дает пример, в котором это необходимо.

\NewDocumentEnvironment {⟨env⟩} {⟨arg spec⟩} {⟨beg-code⟩} {⟨end-code⟩}
\RenewDocumentEnvironment {⟨env⟩} {⟨arg spec⟩} {⟨beg-code⟩} {⟨end-code⟩}
\ProvideDocumentEnvironment {⟨env⟩} {⟨arg spec⟩} {⟨beg-code⟩} {⟨end-code⟩}
\DeclareDocumentEnvironment {⟨env⟩} {⟨arg spec⟩} {⟨beg-code⟩} {⟨end-code⟩}

Эти команды работают так же, как \NewDocumentCommand и т. д., но создают окружения (\begin{⟨env⟩} … \end{⟨env⟩}). Как ⟨beg-code⟩, так и ⟨end-code⟩ могут получать доступ к аргументам, как определено в ⟨arg spec⟩. Аргументы будут переданы после \begin{⟨env⟩}. Все пробелы в начале и в конце {⟨env⟩} удаляются перед определением, таким образом:

\NewDocumentEnvironment{foo}

и

\NewDocumentEnvironment{ foo }

оба создают одно и то же окружение «foo».

2.5 Необязательные аргументы

В отличие от команд, созданных с помощью \newcommand в LaTeX 2ε, необязательные аргументы, созданные с помощью \NewDocumentCommand, могут безопасно вкладываться. Таким образом, например, после

\NewDocumentCommand\foo{om}{I grabbed ‘#1’ and ‘#2’}
\NewDocumentCommand\baz{o}{#1-#1}

использование команды как

\foo[\baz[stuff]]{more stuff}

выведет

I grabbed ‘stuff-stuff’ and ‘more stuff’

Это особенно полезно, когда команда с необязательным аргументом помещается внутри необязательного аргумента второй команды.

Когда необязательный аргумент следует за обязательным аргументом с тем же разделителем, парсер выдает предупреждение, поскольку необязательный аргумент не может быть опущен пользователем, тем самым фактически становясь обязательным. Это может касаться аргументов типов o, d, O, D, s, t, e и E, за которыми следуют обязательные аргументы типа r или R.

Значение по умолчанию для аргументов O, D и E может быть результатом захвата другого аргумента. Таким образом, например,

\NewDocumentCommand\foo{O{#2} m}

будет использовать обязательный аргумент в качестве значения по умолчанию для ведущего необязательного аргумента.

2.6 Пробелы и необязательные аргументы

TeX найдет первый аргумент после имени функции независимо от любых промежуточных пробелов. Это верно как для обязательных, так и для необязательных аргументов. Таким образом, \foo[arg] и \foo␣[arg] эквивалентны. Пробелы также игнорируются при сборе аргументов до последнего обязательного аргумента, который должен существовать. Поэтому после

\NewDocumentCommand\foo{m o m}{ ... }

ввод пользователя \foo{arg1}[arg2]{arg3} и \foo{arg1}␣[arg2]␣{arg3} будут парситься одинаково.

Поведение необязательных аргументов после любых обязательных аргументов можно настроить. Стандартные настройки позволят пробелы здесь, и таким образом, с

\NewDocumentCommand\foobar{m o}{ ... }

как \foobar{arg1}[arg2], так и \foobar{arg1}␣[arg2] найдут необязательный аргумент. Это можно изменить, добавив модификатор ! в спецификацию аргументов:

\NewDocumentCommand\foobar{m !o}{ ... }

где \foobar{arg1}␣[arg2] не найдет необязательный аргумент.

Существует одна тонкость, связанная с различием в обработке TeX «управляющих символов», когда имя команды состоит из одного символа, например, ‘\’. Пробелы здесь не игнорируются TeX, и, таким образом, возможно, чтобы необязательный аргумент следовал непосредственно за такой командой. Наиболее распространенный пример — использование \ в окружениях amsmath, которое в терминах здесь будет определено как

\NewDocumentCommand\\{!s !o}{ ... }

Также стоит отметить, что при использовании необязательных аргументов в последней позиции TeX обязательно будет заглядывать вперед для открытия токена аргумента. Это означает, что значение \inputlineno будет «сдвинуто на один», если такой завершающий необязательный аргумент отсутствует и команда заканчивает строку; оно будет на единицу больше, чем номер строки, содержащей последний обязательный аргумент.

2.7 «Украшения»

Аргумент типа E позволяет задавать одно значение по умолчанию для каждого тестового токена. Это достигается путем указания списка значений по умолчанию для каждой записи в списке, например:

E{^_}{{UP}{DOWN}}

Если список значений по умолчанию короче списка тестовых токенов, будет возвращен специальный маркер -NoValue- (как и для аргумента типа e). Таким образом, например:

E{^_}{{UP}}

имеет значение по умолчанию UP для тестового символа ^, но вернет маркер -NoValue- в качестве значения по умолчанию для _. Это позволяет смешивать явные значения по умолчанию с проверкой на отсутствие значений.

2.8 Проверка специальных значений

Необязательные аргументы используют специальные переменные для возврата информации о природе полученного аргумента.

\IfNoValueTF {⟨arg⟩} {⟨true code⟩} {⟨false code⟩}
\IfNoValueT {⟨arg⟩} {⟨true code⟩}
\IfNoValueF {⟨arg⟩} {⟨false code⟩}

Команды \IfNoValue(TF) используются для проверки, является ли ⟨argument⟩ (#1, #2 и т. д.) специальным маркером -NoValue-. Например:

\NewDocumentCommand\foo{o m} {
  \IfNoValueTF {#1}
    {\DoSomethingJustWithMandatoryArgument{#2}}
    {\DoSomethingWithBothArguments{#1}{#2}}
}

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

Обратите внимание, что доступны три теста, в зависимости от того, какие ветви результата требуются: \IfNoValueTF, \IfNoValueT и \IfNoValueF. Поскольку тесты \IfNoValue(TF) являются расширяемыми, возможно проверять эти значения позже, например, в момент верстки или в контексте расширения.

Важно отметить, что -NoValue- сконструирован таким образом, что он не будет соответствовать простому текстовому вводу -NoValue-, т.е.

\IfNoValueTF{-NoValue-}

будет логически ложным. Когда два необязательных аргумента следуют друг за другом (синтаксис, который мы обычно не рекомендуем), может иметь смысл позволить пользователям команды указывать только второй аргумент, предоставив пустой первый аргумент. Вместо того чтобы отдельно проверять на пустоту и на -NoValue-, лучше использовать тип аргумента O с пустым значением по умолчанию, а затем проверять на пустоту, используя условие \IfBlankTF (описанное ниже).

\IfValueTF {⟨arg⟩} {⟨true code⟩} {⟨false code⟩}
\IfValueT {⟨arg⟩} {⟨true code⟩}
\IfValueF {⟨arg⟩} {⟨false code⟩}

Обратная форма тестов \IfNoValue(TF) также доступна как \IfValue(TF). Контекст определит, какая логическая форма имеет наибольший смысл для данного сценария кода.

\IfBlankTF {⟨arg⟩} {⟨true code⟩} {⟨false code⟩}
\IfBlankT {⟨arg⟩} {⟨true code⟩}
\IfBlankF {⟨arg⟩} {⟨false code⟩}

Команда \IfNoValueTF выбирает ⟨true code⟩, если необязательный аргумент вообще не использовался (и возвращает специальный маркер -NoValue-), но не если ему было дано пустое значение. В отличие от этого, \IfBlankTF возвращает true, если его аргумент либо действительно пуст, либо содержит один или несколько обычных пробелов. Например:

\NewDocumentCommand\foo{m!o}{\par #1: 
	\IfNoValueTF{#2} 
		{No optional}% 
		{% 
	\IfBlankTF{#2} 
		{Blanks in or empty}% 
		{Real content in}% 
		}% 
	\space argument!}

\foo{1}[bar] \foo{2}[ ] \foo{3}[] \foo{4}[\space] \foo{5}[x]

результирует в следующем выводе:

1: Real content in argument!
2: Blanks in or empty argument!
3: Blanks in or empty argument!
4: Real content in argument!
5: No optional argument! [x]

Обратите внимание, что \space в (4) считается реальным содержимым — потому что это команда, а не символ «пробел» — даже если это приводит к созданию пробела. Вы также можете наблюдать в (5) эффект спецификатора !, предотвращающего последнюю \foo от интерпретации [x] как своего необязательного аргумента.

\BooleanFalse
\BooleanTrue

Флаги истинности и ложности, устанавливаемые при поиске необязательного символа (с использованием s или t⟨char⟩), имеют имена, которые доступны вне блоков кода.

\IfBooleanTF {⟨arg⟩} {⟨true code⟩} {⟨false code⟩}
\IfBooleanT {⟨arg⟩} {⟨true code⟩}
\IfBooleanF {⟨arg⟩} {⟨false code⟩}

Эти команды используются для проверки, является ли ⟨argument⟩ (#1, #2 и т. д.) равным \BooleanTrue или \BooleanFalse. Например:

\NewDocumentCommand\foo{sm} {
  \IfBooleanTF {#1}
    {\DoSomethingWithStar{#2}}
    {\DoSomethingWithoutStar{#2}}
}

Этот код проверяет наличие звезды в качестве первого аргумента, а затем выбирает действие на основе этой информации.

2.9 Автоматическое преобразование в формат ключ-значение

Некоторые команды документа имеют долгую историю принятия необязательного аргумента в виде «свободного текста», например, \caption и команды секционирования \section и т.д. Введение более сложных (keyval) опций для этих команд требует метода интерпретации необязательного аргумента как свободного текста или как серии ключевых значений. Это должно происходить во время захвата аргумента, так как необходимо аккуратно обрабатывать фигурные скобки для получения правильного результата.

Модификатор = доступен для того, чтобы позволить ltcmd правильно реализовать этот процесс. Модификатор гарантирует, что аргумент будет передан в дальнейший код как серия ключевых значений. Для этого = должен быть за ним, за которым следует аргумент, содержащий имя ключа по умолчанию. Это используется как ключ в паре ключ-значение, если «сырой» аргумент не имеет правильной формы для интерпретации как набора ключевых значений.

Пример с \caption

Рассмотрим \caption в качестве примера с демонстрационной реализацией:

\DeclareDocumentCommand \caption {s ={short-text} +O{#3} +m} {%
  \showtokens{Grabbed arguments:^^J(#2)^^Jand^^J(#3)}%
}

Имя ключа по умолчанию — short-text. Когда команда \caption используется, если необязательный аргумент является свободным текстом, например:

\caption[Некоторый короткий текст]{Длинный и более детализированный текст для демонстрационных целей}

то вывод будет:

Grabbed arguments: (short-text={Некоторый короткий текст}) and (Длинный и более детализированный текст для демонстрационных целей)

С другой стороны, если заголовок задан с аргументом в формате ключ-значение:

\caption[label = cap:demo]{Длинный и более детализированный текст для демонстрационных целей}

то это будет учтено:

Grabbed arguments: (label = cap:demo) and (Длинный и более детализированный текст для демонстрационных целей)

Интерпретация в формате ключ-значение

Интерпретация как ключ-значение определяется наличием символов = в аргументе. Символы в режиме встроенной математики (включенные в $...$ или \(...\)) игнорируются. Аргумент можно заставить быть прочитанным как ключ-значение, включив пустую запись в начале:

\caption[=,Это теперь ключ-значение]%
\caption[Это не $=$ ключ-значение]%

Эта пустая запись не передается в основной код, поэтому не приведет к проблемам с парсерами ключ-значение, которые не допускают пустое имя ключа. Любые знаки = в текстовом режиме необходимо обрамлять фигурными скобками, чтобы избежать неправильной интерпретации: это, вероятно, наиболее удобно обрабатывать, обрамляя весь аргумент:

\caption[{Не = ключ-значение!}]%

что будет правильно передано как:

Grabbed arguments: (short-text = {Не = ключ-значение!})

2.10 Процессоры аргументов

Процессоры аргументов применяются к аргументу после его захвата основной системой, но перед передачей в ⟨код⟩. Таким образом, процессор аргументов может использоваться для нормализации ввода на ранней стадии, позволяя внутренним функциям быть полностью независимыми от формы ввода. Процессоры применяются к пользовательскому вводу и к значениям по умолчанию для необязательных аргументов, но не к специальному маркеру -NoValue-.

Каждый процессор аргументов указывается синтаксисом >{⟨процессор⟩} в спецификации аргумента. Процессоры применяются справа налево, так что >{\ProcessorB} >{\ProcessorA} m применит \ProcessorA, а затем \ProcessorB к токенам, захваченным аргументом m.

\SplitArgument {⟨number⟩} {⟨token(s)⟩}

Этот процессор разделяет аргумент, заданный при каждом вхождении ⟨tokens⟩, до максимума ⟨number⟩ токенов (тем самым деля ввод на ⟨number⟩ + 1 частей). Ошибка будет выдана, если в вводе присутствует слишком много ⟨токенов⟩. Обработанный ввод помещается внутри ⟨number⟩ + 1 наборов фигурных скобок для дальнейшего использования. Если в аргументе меньше, чем ⟨number⟩, то в конце обработанного аргумента добавляются маркеры -NoValue-.

\NewDocumentCommand \foo {>{\SplitArgument{2}{;}} m} {\InternalFunctionOfThreeArguments#1}

Если для разделения используется только один символ ⟨токен⟩, любой символ с кодом категории 13 (активный) совпадающий с ⟨токеном⟩ будет заменен до того, как произойдет разделение. Пробелы обрезаются в начале и в конце каждого элемента, который разбирается.

Тип аргумента E несколько особенный, потому что с одним E в объявлении команды вы можете получить несколько аргументов в команде (по одному формальному аргументу на каждый токен украшения). Поэтому, когда процессор аргументов применяется к аргументу типа e/E, все аргументы проходят через этот процессор перед тем, как быть переданными в ⟨код⟩. Например, эта команда:

\NewDocumentCommand \foo { >{\TrimSpaces} e{_^} } { [#1](#2) }

применяет \TrimSpaces к обоим аргументам.

\SplitList {⟨токены⟩}

Этот процессор разделяет аргумент, заданный при каждом вхождении ⟨токенов⟩, где количество элементов не фиксировано. Каждый элемент затем оборачивается в фигурные скобки внутри #1. Результат заключается в том, что обработанный аргумент может быть дополнительно обработан с использованием функции отображения (см. ниже).

\NewDocumentCommand \foo {>{\SplitList{;}} m} {\MappingFunction#1}

Если для разделения используется только один символ ⟨токен⟩, он учтет возможность того, что ⟨токен⟩ был активирован (код категории 13) и будет разделять по таким токенам. Пробелы обрезаются в начале и в конце каждого элемента, который разбирается. Точно один набор фигурных скобок будет удален, если весь элемент окружен ими, т.е. следующие вводы и выводы приводят к результату (каждый отдельный элемент как группа фигурных скобок):

a ==> {a}
{a} ==> {a}
{a}b ==> {{a}b}
a,b ==> {a}{b}
{a},b ==> {a}{b}
a,{b} ==> {a}{b}
a,{b}c ==> {a}{{b}c}

\ProcessList {⟨список⟩} {⟨токены⟩}

Чтобы поддержать \SplitList, доступна функция \ProcessList, которая применяет ⟨токены⟩ ко всем элементам в ⟨списке⟩. ⟨Токены⟩ могут содержать произвольные данные, которые ожидают один аргумент после них: элемент списка. Например:

\NewDocumentCommand \foo {>{\SplitList{;}} m} 
	{\ProcessList{#1}{\SomeDocumentCommand}}

или

\NewDocumentCommand \foo {>{\SplitList{;}} m} 
	{\ProcessList{#1}{Abc \SomeDocumentCommand}}

\ReverseBoolean

Этот процессор изменяет логику \BooleanTrue и \BooleanFalse, так что пример из предыдущего раздела будет выглядеть следующим образом:

\NewDocumentCommand\foo{>{\ReverseBoolean} s m} {%
  \IfBooleanTF#1% 
    {\DoSomethingWithoutStar{#2}}% 
    {\DoSomethingWithStar{#2}}% 
}

\TrimSpaces

Удаляет любые ведущие и завершающие пробелы (токены с кодом символа 32 и кодом категории 10) на концах аргумента. Таким образом, например, объявление функции:

\NewDocumentCommand\foo {>{\TrimSpaces} m} 
	{\showtokens{#1}}

и использование ее в документе как:

\foo{␣hello␣world␣}

покажет «hello␣world» в терминале, при этом пробелы в начале и в конце будут удалены. \TrimSpaces удалит множественные пробелы с концов ввода в случаях, когда они были включены так, что стандартное преобразование TEX, которое сводит множественные пробелы к одному, не применяется.

2.11 Тело окружения

Хотя окружения \begin{⟨окружение⟩} ... \end{⟨окружение⟩} обычно используются в случаях, когда код, реализующий ⟨окружение⟩, не нуждается в доступе к содержимому окружения (его «телу»), иногда полезно иметь тело в качестве стандартного аргумента. Это достигается путем завершения спецификации аргумента с помощью b, который является специальным типом аргумента для этой ситуации. Например:

\NewDocumentEnvironment{twice} {O{\ttfamily} +b} {#2#1#2} {}
\begin{twice}[\itshape] 
Hello world! 
\end{twice}

выводит «Hello world!Hello world!».

Префикс + используется для разрешения нескольких абзацев в теле окружения. Процессоры аргументов также могут применяться к аргументам типа b. По умолчанию пробелы обрезаются в начале и в конце тела: в приведенном примере в противном случае будут пробелы, исходящие из концов строк после [\itshape] и world!. Добавление префикса ! перед b подавляет обрезку пробелов.

Когда b используется в спецификации аргумента, последний аргумент объявления окружения (например, \NewDocumentEnvironment), который состоит из ⟨кода завершения⟩ для вставки в \end{⟨окружение⟩}, является избыточным, так как можно просто поместить этот код в конец ⟨кода начала⟩. Тем не менее, этот (пустой) ⟨код завершения⟩ должен быть предоставлен.

Окружения, использующие эту функцию, могут быть вложенными.

2.12 Полностью расширяемые команды документа

Команды документа, созданные с помощью \NewDocumentCommand и т.д., обычно создаются так, чтобы они не расширялись неожиданно. Это достигается с использованием возможностей движка, поэтому это более мощно, чем механизм \protect в LATEX 2ε. Существуют очень редкие случаи, когда может быть полезно создать функции с использованием захватчика, который работает только на расширении. Это накладывает ряд ограничений на природу аргументов, принимаемых функцией, и на код, который она реализует. Эта возможность должна использоваться только в случае необходимости.

Команды для создания полностью расширяемых команд

\NewExpandableDocumentCommand {⟨cmd⟩} {⟨arg spec⟩} {⟨code⟩}
\RenewExpandableDocumentCommand {⟨cmd⟩} {⟨arg spec⟩} {⟨code⟩}
\ProvideExpandableDocumentCommand {⟨cmd⟩} {⟨arg spec⟩} {⟨code⟩}
\DeclareExpandableDocumentCommand {⟨cmd⟩} {⟨arg spec⟩} {⟨code⟩}

Эта группа команд используется для создания команды документа уровня ⟨cmd⟩, которая будет захватывать свои аргументы в полностью расширяемом режиме. Спецификация аргументов для функции задается с помощью ⟨arg spec⟩, а ⟨cmd⟩ будет выполнять ⟨code⟩. В общем, ⟨code⟩ также будет полностью расширяемым, хотя возможно, что это не так (например, функция для использования в таблице может расширяться так, что \omit будет первым нерасширяемым не-пробельным токеном).

Ограничения при парсинге аргументов

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

  • Последний аргумент (если таковой имеется) должен быть одним из обязательных типов m, r или R.
  • Аргумент типа «вербатим» v недоступен.
  • Процессоры аргументов (с использованием >) недоступны.
  • Невозможно различить, например, \foo[ и \foo{[}: в обоих случаях [ будет интерпретироваться как начало необязательного аргумента. В результате проверка на необязательные аргументы менее надежна, чем в стандартной версии.

2.13 Команды в начале ячеек таблицы

Создание команд, которые используются в начале ячеек таблицы, накладывает некоторые ограничения на основную реализацию. Стандартные окружения LATEX для таблиц (такие как tabular и т.д.) используют механизм, который требует, чтобы любая команда, оборачивающая \multicolumn или подобные команды, была «расширяемой». Это не относится к командам, созданным с помощью \NewDocumentCommand и т.д., которые, как описано в разделе 2.12, используют функцию движка, предотвращающую такое «расширение».

Поэтому, чтобы создать такие обертки для использования в начале ячеек таблицы, необходимо использовать \NewExpandableDocumentCommand. Например:

\NewExpandableDocumentCommand\MyMultiCol{m}{\multicolumn{3}{c}{#1}}
\begin{tabular}{lcr}
  a & b & c \\
  \MyMultiCol{stuff} \\
\end{tabular}

В этом примере команда \MyMultiCol позволяет использовать \multicolumn в ячейках таблицы, обеспечивая необходимую расширяемость.

2.14 Использование аргументов типа verbatim

Как описано выше, аргумент типа v можно рассматривать как аналог команды \verb. Прежде чем рассмотреть, что это именно означает, важно выделить некоторые ключевые различия. Прежде всего, захват аргумента, подобного verbatim, отделен от его наборки: последнее будет рассмотрено в следующем разделе.

При захвате аргумента типа v LATEX сначала использует команду ядра \dospecials, чтобы отключить «специальный» характер символов. Затем он делает как пробелы, так и табуляции «активными», чтобы им можно было задать пользовательское определение. Все остальные символы захватываются как есть: это означает, что если какие-либо символы были сделаны «специальными» и не перечислены в \dospecials, возникнет ошибка (см. ниже).

Символы, которые захватываются как аргумент, — это все те, что находятся между двумя одинаковыми символами: в отличие от \verb, символы \,, {, } и % не могут использоваться в качестве разделителей. Если любой из захваченных токенов имеет «специальное» значение, будет выдана ошибка.

Для аргумента типа +v, который позволяет переносить строки внутри аргумента, символы новой строки преобразуются в команды \obeyedline. Стандартное определение \obeyedline — это простая команда \par, что позволяет захваченным токенам использоваться непосредственно в наборе. Локальное переопределение \obeyedline может быть использовано для достижения других результатов. Например, чтобы сохранить пустые строки при наборе, можно использовать:

\renewcommand*\obeyedline{\mbox{}\par}

Дополнительная информация о использовании этих аргументов в наборе представлена в следующем подразделе.

Некоторые дополнительные детали, которые могут быть полезны для тех, кто имеет больше знаний о TEX: не беспокойтесь, если это не имеет смысла для вас! Пробелы и табуляции хранятся как активные символы. В 8-битных движках не-ASCII символы являются «активными», в то время как все символы ASCII, кроме букв a–zA–Z, являются «другими». В Юникодных движках не-ASCII кодовые точки будут либо буквами, либо «другими», в зависимости от стандартных настроек LATEX, основанных на данных Юникода. Для сравнений на основе токенов, вероятно, активные пробелы и табуляции должны быть заменены: это можно удобно сделать с помощью расширения.

2.15 Набор материала, подобного verbatim

В отличие от \verb, аргумент типа (+)v касается только захвата аргумента, а не его набора. Таким образом, функции, которые пользователи часто ассоциируют с «verbatim», не активируются автоматически, например, выбор моноширинного шрифта. Материал, захваченный аргументом типа v, не подавляет автоматически лигатуры: с современными движками TEX это в значительной степени можно сделать без манипуляций с токенами, которые использует \verb. (В \verb лигатуры подавляются путем активации символов и вставки нулевой ширины керна перед самим символом.)

Команда \verb также выбирает моноширинный шрифт: это не является внутренним для verbatim-материала, поэтому его нужно настраивать, например, с помощью \ttfamily. Аналогично, окружение verbatim настраивает значение \par, подходящее для переноса строк.

2.16 Окружения verbatim

В некоторых случаях, когда вы захватываете тело окружения, вы захотите, чтобы содержимое обрабатывалось как verbatim. Это доступно с использованием спецификации аргумента c. Как и спецификация b, это должно быть последним. Таким образом, например:

\NewDocumentEnvironment{MyVerbatim}{!O{\ttfamily} c} {\begin{center} #1 #2\end{center}} {}
\begin{MyVerbatim}[\ttfamily\itshape]
% Some code is shown here
$y = mx + c$
\end{MyVerbatim}

будет выводить содержимое verbatim, таким образом:

␣␣%␣Some␣code␣is␣shown␣here
␣␣$y␣=␣mx␣+␣c$

Поскольку захват всего содержимого verbatim приведет к отсутствию токенов \par, новые строки всегда разрешены: здесь нет необходимости в модификаторе +. Как и в спецификации v, новые строки хранятся как \obeyedline. Аналогично спецификации b, по умолчанию новые строки обрезаются в начале и в конце тела. Добавление префикса ! перед c подавляет эту обрезку.

Сбор тела происходит построчно: содержимое собирается до конца строки в исходном коде, затем проверяется перед хранением. Это означает, что строка, завершающая окружение (содержащая в приведенном выше примере \end{MyVerbatim}), не может содержать текст после конца окружения. Текст перед концом окружения обрабатывается нормально, но обратите внимание, что если здесь есть текст, то не добавляется завершающий \obeyedline. Кроме необязательных аргументов, текст не допускается на открывающей строке окружения.

Специальная обработка применяется к аргументам с спецификацией o, O, d или D, которые идут сразу перед спецификацией c. Это означает, что когда необязательный аргумент отсутствует, первый символ следующей строки будет прочитан с правильно примененным кодом категории verbatim. Проблемы могут возникнуть, если несколько необязательных аргументов используются перед спецификацией c: это будет работать надежно только в тех случаях, когда необязательные токены являются «другими» символами.

По техническим причинам мы рекомендуем не игнорировать пробелы при поиске необязательного аргумента перед спецификацией c: это можно сделать, добавив модификатор !, как показано в примере. Однако это оставлено на усмотрение пользователя.

2.17 Производительность

Для команд документа, где спецификация аргументов полностью состоит из записей m или +m (или полностью пуста), внутренняя структура, создаваемая с помощью \NewDocumentCommand, по сути, так же эффективна, как и предоставляемая \newcommand(*). Таким образом, команды документа могут заменять конструкции, возникающие из \newcommand и т.д., без необходимости беспокоиться о производительности. Следует отметить, что \newcommand(*) производит расширяемые результаты, поэтому прямой заменой является \NewExpandableDocumentCommand; однако в большинстве случаев лучше использовать \NewDocumentCommand, чтобы обеспечить более надежные структуры.

2.18 Подробности о разделителях аргументов

В обычных (нерасширяемых) командах делимитированные типы ищут начальный разделитель, заглядывая вперед (с использованием функций \peek_... из expl3), ища токен-разделитель. Токен должен иметь то же значение и «форму», что и токен, определенный как разделитель. Существует три возможных случая разделителей: символы, управляющие последовательности и активные символы. Для всех практических целей этого описания активные символы будут вести себя точно так же, как управляющие последовательности.

2.18.1 Символьные токены

Символьный токен характеризуется своим кодом символа, а его значение — кодом категории (\catcode). Когда команда определяется, значение символьного токена фиксируется в определении команды и не может изменяться. Команда правильно увидит разделитель аргумента, если открывающий разделитель имеет те же коды символа и категории, что и в момент определения. Например:

\NewDocumentCommand { \foobar } { D<>{default} } {(#1)}
\foobar <hello> \par
\char_set_catcode_letter:N <
\foobar <hello>

вывод будет:

(hello) 
(default)<hello>

так как открывающий разделитель < изменил свое значение между двумя вызовами \foobar, поэтому второй вызов не видит < как допустимый разделитель. Команды предполагают, что если был найден допустимый открывающий разделитель, то также будет соответствующий закрывающий разделитель. Если его нет (либо из-за пропуска, либо из-за изменения значения), возникает ошибка низкого уровня TEX, и вызов команды прерывается.

2.18.2 Токены управляющих последовательностей

Токен управляющей последовательности (или управляющего символа) характеризуется своим именем, а его значение — это его определение. Токен не может иметь два разных значения одновременно. Когда управляющая последовательность определяется как разделитель в команде, она будет обнаружена как разделитель всякий раз, когда имя управляющей последовательности встречается в документе, независимо от ее текущего определения. Например:

\cs_set:Npn \x { abc }
\NewDocumentCommand { \foobar } { D\x\y{default} } {(#1)}
\foobar \x hello\y \par
\cs_set:Npn \x { def }
\foobar \x hello\y

вывод будет:

(hello) 
(hello)

при этом оба вызова команды видят разделитель \x.

2.19 Создание новых процессоров аргументов

\ProcessedArgument

Процессоры аргументов позволяют манипулировать захваченным аргументом перед его передачей в основной код. Новые реализации процессоров могут быть созданы в виде функций, которые принимают один завершающий аргумент и оставляют свой результат в переменной \ProcessedArgument.

Например, \ReverseBoolean определяется следующим образом:

\ExplSyntaxOn
\cs_new_protected:Npn \ReverseBoolean #1 {
  \bool_if:NTF #1 {
    \tl_set:Nn \ProcessedArgument { \c_false_bool }
  } {
    \tl_set:Nn \ProcessedArgument { \c_true_bool }
  }
}
\ExplSyntaxOff

Примечание: Код написан на языке expl3, поэтому нам не нужно беспокоиться о том, что пробелы могут попасть в определение.

3 Копирование и отображение (робустных) команд и окружений

Если вы хотите (слегка) изменить существующую команду, вам может понадобиться сохранить текущее определение под новым именем, а затем использовать его в новом определении. Если существующая команда является робустной, то старый трюк с использованием низкоуровневой команды \let не сработает, потому что он копирует только верхнее определение, но не ту часть, которая фактически выполняет работу. Поскольку большинство команд LATEX в настоящее время являются робустными, LATEX предлагает некоторые высокоуровневые объявления для этого.

Однако, пожалуйста, обратите внимание, что обычно лучше использовать доступные хуки (например, хуки для общих команд или окружений), вместо того чтобы копировать текущее определение и тем самым замораживать его; смотрите документацию по управлению хуками lthooks-doc.pdf для получения подробной информации.

\NewCommandCopy {⟨cmd⟩} {⟨existing-cmd⟩} 
\RenewCommandCopy {⟨cmd⟩} {⟨existing-cmd⟩} 
\DeclareCommandCopy {⟨cmd⟩} {⟨existing-cmd⟩} 

Эти команды копируют определение ⟨existing-cmd⟩ в ⟨cmd⟩. После этого ⟨existing-cmd⟩ может быть переопределен, и ⟨cmd⟩ все еще будет работать! Это позволяет вам предоставить новое определение для ⟨existing-cmd⟩, которое использует ⟨cmd⟩ (т.е. его старое определение). Например, после

\NewCommandCopy\LaTeXorig\LaTeX
\RenewDocumentCommand\LaTeX{}{\textcolor{blue}{\LaTeXorig}}

все логотипы LATEX, сгенерированные с помощью \LaTeX, будут отображаться синим цветом (при условии, что у вас загружен пакет цвета).

Различия между \New... и \Renew... такие же, как и в других случаях: вы получите ошибку в зависимости от того, существует ли уже ⟨cmd⟩, или в случае \Declare... оно будет скопировано независимо от этого. Обратите внимание, что нет объявления \Provide..., потому что это было бы ограниченной ценностью.

Если ⟨cmd⟩ или ⟨existing-cmd⟩ не могут быть предоставлены как единый токен, а требуют “конструирования”, вы можете использовать \ExpandArgs, как объясняется в разделе 4.

\ShowCommand {⟨cmd⟩}

Эта команда отображает значение ⟨cmd⟩ в терминале и затем останавливается (так же, как и примитивная \show). Разница в том, что она правильно показывает значение более сложных команд, например, в случае робустных команд она отображает не только верхнее определение, но и фактический код нагрузки, а в случае команд, объявленных с помощью \NewDocumentCommand и т.д., она также предоставляет подробную информацию о сигнатуре аргументов.

\NewEnvironmentCopy {⟨env⟩} {⟨existing-env⟩} 
\RenewEnvironmentCopy {⟨env⟩} {⟨existing-env⟩} 
\DeclareEnvironmentCopy {⟨env⟩} {⟨existing-env⟩} 

Эти команды копируют определение окружения ⟨existing-env⟩ в ⟨env⟩ (как начальный, так и конечный код), т.е. это просто применение \NewCommandCopy дважды к внутренним командам, которые определяют окружение, т.е. \⟨env⟩ и \end⟨env⟩. Различия между \New..., \Renew... и \Declare... являются обычными.

\ShowEnvironment {⟨env⟩}

Эта команда отображает значение начального и конечного кода для окружения ⟨env⟩.

4 Предварительное построение имен команд (или иное расширение аргументов)

При объявлении новых команд с помощью \NewDocumentCommand, \NewCommandCopy или аналогичных команд иногда необходимо “построить” имя команды. В качестве общего механизма L3 программный уровень предлагает \exp_args:N... для этой цели, но нет механизма для этого, если \ExplSyntaxOn не активен (и смешивание команд программного и пользовательского уровней не является хорошим подходом). Поэтому мы предлагаем механизм для доступа к этой возможности с использованием именования в стиле CamelCase.

\UseName {⟨string⟩} 
\ExpandArgs {⟨spec⟩} {⟨cmd⟩} {⟨arg1⟩} . . .

\UseName преобразует ⟨string⟩ непосредственно в имя команды и затем выполняет его: это эквивалентно давно существующей внутренней команде LATEX 2ε \@nameuse или эквиваленту L3 программирования \use:c. \ExpandArgs принимает ⟨spec⟩, который описывает, как расширять ⟨arguments⟩, выполняет эти операции, а затем выполняет ⟨cmd⟩. ⟨spec⟩ использует описания, предлагаемые L3 программным уровнем, и соответствующая функция \exp_args:N... должна существовать. Общие случаи будут иметь ⟨spec⟩ в виде c, cc или Nc: см. ниже.

В качестве примера, следующее объявление предоставляет метод для генерации команд редактирования:

\NewDocumentCommand\newcopyedit{mO{red}} {
  \newcounter{todo#1}%
  \ExpandArgs{c}\NewDocumentCommand{#1}{s m} {
    \stepcounter{todo#1}%
    \IfBooleanTF {##1} {
      \todo[color=#2!10]{\UseName{thetodo#1}: ##2}%
    } {
      \todo[inline,color=#2!10]{\UseName{thetodo#1}: ##2}%
    }%
  }%
}

С учетом этого объявления вы можете написать \newcopyedit{note}[blue], что определит команду \note и соответствующий счетчик для вас.

Второй пример — это копирование команды по строковому имени с использованием \NewCommandCopy: здесь нам может понадобиться построить оба имени команд.

\NewDocumentCommand\savebyname{m} {
  \ExpandArgs{cc}\NewCommandCopy{saved#1}{#1}
}

В ⟨spec⟩ каждая c обозначает один аргумент, который преобразуется в команду. n представляет собой “нормальный” аргумент, который не изменяется, а N обозначает “нормальный” аргумент, который также остается неизменным, но состоит только из одного токена (и обычно не заключен в фигурные скобки). Таким образом, чтобы построить команду из строки только для второго аргумента \NewCommandCopy, вы бы написали:

\ExpandArgs{Nc}\NewCommandCopy\mysectionctr{c@section}

Существует несколько других одиночных букв, поддерживаемых в L3 программном уровне, которые могут быть использованы в ⟨spec⟩ для манипуляции аргументами другими способами. Если вас это интересует, ознакомьтесь с разделом “Расширение аргументов” в документации L3 программного уровня в файле interface3.pdf.

5 Расширяемые вычисления с плавающей точкой (и другие)

Программный уровень LATEX3, который является частью формата, предлагает богатый интерфейс для манипуляции переменными и значениями с плавающей точкой. Чтобы позволить (более простым) приложениям использовать это на уровне документа или в пакетах, которые иначе не используют L3 программный уровень, предоставляется несколько интерфейсных команд.

\fpeval {⟨floating point expression⟩}

Расширяемая команда \fpeval принимает в качестве аргумента выражение с плавающей точкой и производит результат, используя обычные правила математики. Поскольку эта команда является расширяемой, ее можно использовать там, где TEX требует числа, например, в низкоуровневой операции \edef, чтобы получить чисто числовой результат.

Кратко, выражения с плавающей точкой могут включать:

  • Основные арифметические операции: сложение x + y, вычитание x - y, умножение x * y, деление x / y, квадратный корень sqrt x и скобки.
  • Операторы сравнения: x < y, x <= y, x > y, x != y и т.д. Отношение x ? y истинно, если один или оба операнда являются NaN или являются кортежем, если только они не равны кортежам. Каждое ⟨отношение⟩ может быть любой (непустой) комбинацией <, =, >, и ?, плюс необязательный ведущий ! (который отрицает ⟨отношение⟩), с ограничением, что отрицательное ⟨отношение⟩ не может начинаться с ?.
  • Булева логика: знак sign x, отрицание ! x, конъюнкция x && y, дизъюнкция x || y, тернарный оператор x ? y : z.
  • Экспоненты: exp x, ln x, xˆy.
  • Целочисленный факториал: fact x.
  • Тригонометрия: sin x, cos x, tan x, cot x, sec x, csc x, ожидая, что их аргументы будут в радианах, и sind x, cosd x, tand x, cotd x, secd x, cscd x, ожидая, что их аргументы будут в градусах.
  • Обратные тригонометрические функции: asin x, acos x, atan x, acot x, asec x, acsc x, дающие результат в радианах, и asind x, acosd x, atand x, acotd x, asecd x, acscd x, дающие результат в градусах.
  • Экстремумы: max(x1, x2, ...), min(x1, x2, ...), abs(x).
  • Функции округления, управляемые двумя необязательными значениями, n (количество знаков, по умолчанию 0) и t (поведение при равенстве, по умолчанию NaN):
    • trunc(x, n) округляет к нулю,
    • floor(x, n) округляет к −∞,
    • ceil(x, n) округляет к +∞,
    • round(x, n, t) округляет к ближайшему значению, при равенстве округляя к четному значению по умолчанию, к нулю, если t = 0, к +∞, если t > 0, и к −∞, если t < 0.
  • Случайные числа: rand(), randint(m, n).
  • Константы: pi, deg (один градус в радианах).
  • Размерности, автоматически выраженные в пунктах, например, pc равен 12.
  • Автоматическое преобразование (нет необходимости в \number) целочисленных, размерных и пропускных переменных в числа с плавающей точкой, выражая размерности в пунктах и игнорируя компоненты растяжения и сжатия пропусков.
  • Кортежи: (x1, ..., xn), которые могут быть сложены, умножены или разделены на число с плавающей точкой, а также могут быть вложенными. Пример использования может быть следующим:
\LaTeX{} может теперь вычислить: $ \frac{\sin (3.5)}{2} + 2\cdot 10^{-3} = \fpeval{sin(3.5)/2 + 2e-3} $.

Это приводит к следующему результату:

LATEX может теперь вычислить: sin(3.5) / 2 + 2 · 10−3 = −0.1733916138448099.

\inteval {⟨integer expression⟩}

Расширяемая команда \inteval принимает в качестве аргумента целочисленное выражение и производит результат, используя обычные правила математики с некоторыми ограничениями, см. ниже. Признаваемые операции: +, -, * и /, а также скобки. Поскольку эта команда является расширяемой, ее можно использовать там, где TEX требует числа, например, в низкоуровневой операции \edef, чтобы получить чисто числовой результат.

Это, по сути, тонкая оболочка для примитивной команды \numexpr, и поэтому имеет некоторые синтаксические ограничения. Эти ограничения следующие:

  • / обозначает деление, округленное до ближайшего целого числа, при этом при равенстве округление происходит от нуля;
  • возникает ошибка, и общее выражение оценивается в ноль, когда абсолютное значение любого промежуточного результата превышает (2^{31} - 1), за исключением случаев операций масштабирования (ab/c), для которых (ab) может быть произвольно большим;
  • скобки не могут появляться после унарных + или -, а именно, размещение +( или -( в начале выражения или после +, -, *, / или ( приводит к ошибке.

Пример использования может быть следующим:

\LaTeX{} может теперь вычислить: Сумма чисел равна $\inteval{1 + 2 + 3}$.

Это приводит к результату:

LATEX может теперь вычислить: Сумма чисел равна 6.
\dimeval {⟨dimen expression⟩} 
\skipeval {⟨skip expression⟩}

Эти команды аналогичны \inteval, но вычисляют длину (dimen) или значение резинки (skip). Обе команды являются тонкими оболочками вокруг соответствующих примитивов движка, что делает их быстрыми, но, следовательно, они имеют те же синтаксические особенности, как обсуждалось выше. Тем не менее, на практике они обычно достаточно эффективны. Например:

\NewDocumentCommand\calculateheight{m}{%
  \setlength\textheight{\dimeval{\topskip+\baselineskip*\inteval{#1-1}}}
}

Эта команда устанавливает \textheight на соответствующее значение, если страница должна содержать определенное количество строк текста. Таким образом, после вызова \calculateheight{40} значение будет установлено на 478.0pt, учитывая значения \topskip (10.0pt) и \baselineskip (12.0pt) в текущем документе.

6 Расширяемый эквивалент \input

\expandableinput {⟨filename⟩}

Определение \input в LATEX не может быть использовано в местах, где TEX выполняет расширение: классический пример — в начале ячейки таблицы. Существует несколько причин для этого: ключевыми являются то, что \input фиксирует, какие файлы были прочитаны, и предоставляет хуки до и после чтения файла.

Чтобы поддержать необходимость выполнения ввода файла в контекстах расширения, доступна команда \expandableinput: она пропускает запись имени файла и не применяет никакие хуки для файлов, но в остальном ведет себя как \input. В частности, она по-прежнему использует \input@path при выполнении поиска файла.

7 Изменение регистра

\MakeUppercase [⟨keyvals⟩] {⟨text⟩} 
\MakeLowercase [⟨keyvals⟩] {⟨text⟩} 
\MakeTitlecase [⟨keyvals⟩] {⟨text⟩} 

TEX предоставляет две примитивные команды \uppercase и \lowercase для изменения регистра текста. Однако у них есть ряд ограничений: они изменяют регистр только явных символов, не учитывают окружающий контекст, не поддерживают ввод UTF-8 с 8-битными движками и т.д. Чтобы преодолеть эту проблему, LATEX предоставляет команды \MakeUppercase, \MakeLowercase и \MakeTitlecase: они предлагают значительное улучшение по сравнению с примитивами TEX. Эти команды являются робустными (\protected) и могут использоваться в движущихся аргументах.

Изменение регистра в общем смысле хорошо понимается в разговорной речи. Заглавный регистр здесь следует определению, данному Консорциумом Unicode: первый символ входных данных будет преобразован в (широком смысле) заглавный регистр, а остальные символы — в строчный. Полный диапазон ввода Unicode UTF-8 может быть поддержан.

\MakeUppercase{hello WORLD ßüé}  % HELLO WORLD SSÜÉ
\MakeLowercase{hello WORLD ßüé}   % hello world ßüé
\MakeTitlecase{hello WORLD ßüé}    % Hello WORLD ßüé

Команды изменения регистра принимают необязательный аргумент, который можно использовать для настройки вывода. Этот необязательный аргумент принимает ключ locale, также доступный под псевдонимом lang, который можно использовать для указания идентификатора языка в формате BCP-47. Это затем применяется для выбора языковых особенностей при изменении регистра.

Для заглавного регистра также могут использоваться ключевые слова: это выбор между first или all. Стандартная настройка — first, что означает, что только самый первый “буквенный” символ будет (широком смысле) преобразован в заглавный регистр. Альтернатива, all, означает, что входные данные делятся по каждому пробелу, и для каждого полученного слова первый символ будет преобразован в заглавный регистр. Например:

\MakeTitlecase[words=first]{some words}  % Some words
\MakeTitlecase[words=all]{some words}    % Some Words

Входные данные, переданные этим командам, “расширяются” перед применением изменения регистра. Это означает, что любые команды внутри входных данных, которые преобразуются в чистый текст, будут изменены по регистру. Математическое содержимое автоматически исключается, как и аргументы команд \label, \ref, \cite, \begin и \end. Дополнительные исключения могут быть добавлены с помощью команды \AddToNoCaseChangeList. Входные данные могут быть исключены из изменения регистра с помощью команды \NoCaseChange.

\MakeUppercase{Some text $y = mx + c$}  % SOME TEXT y = mx + c
\MakeUppercase{\NoCaseChange{iPhone}}    % iPhone

Чтобы позволить использовать робустные команды внутри изменения регистра и получить ожидаемый вывод, доступны две дополнительные управляющие команды. \CaseSwitch позволяет пользователю указать результат для четырех возможных случаев:

  • Без изменения регистра
  • Преобразование в заглавный регистр
  • Преобразование в строчный регистр
  • Заглавный регистр (применяется только к началу входных данных)

Команда \DeclareCaseChangeEquivalent предоставляет способ заменить команду альтернативной версией, когда она встречается внутри ситуации изменения регистра. Существуют три команды для настройки изменения регистра кодовых точек:

\DeclareLowercaseMapping [⟨locale⟩] {⟨codepoint⟩} {⟨output⟩} 
\DeclareTitlecaseMapping [⟨locale⟩] {⟨codepoint⟩} {⟨output⟩} 
\DeclareUppercaseMapping [⟨locale⟩] {⟨codepoint⟩} {⟨output⟩} 

Все три принимают ⟨codepoint⟩ (в виде целочисленного выражения) и приводят к тому, что

8 Поддержка решения проблем

\listfiles [⟨options⟩]

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

Иногда может случиться так, что в файл пакета или класса (или, скорее, в его копию) были внесены локальные изменения. Чтобы позволить идентифицировать такие случаи, \listfiles принимает необязательный аргумент, который позволяет настроить выводимую информацию с использованием подхода ключ-значение:

  • hashes — добавляет MD5-хеш для каждого файла в выводимую информацию.
  • sizes — добавляет размер файла для каждого файла в выводимую информацию.

Обратите внимание, что так как Windows и Unix используют разные окончания строк (LF против LF CR), хеши и размеры файлов из двух систем не будут одинаковыми. Поэтому следует сравнивать эти значения между операционными системами одного типа.

Предупреждение: эта команда будет перечислять только файлы, которые были прочитаны с использованием команд LATEX, таких как \input{⟨file⟩} или \include{⟨file⟩}. Если файл был прочитан с использованием примитивного синтаксиса TEX \input file (без фигурных скобок вокруг имени файла), то он не будет перечислен; несоблюдение формата LATEX с фигурными скобками может вызвать более серьезные проблемы, возможно, приведя к перезаписи важных файлов, поэтому всегда используйте фигурные скобки.

1 - Памятка по командам Latex 3

Краткий справочник по командам LAtex 3 для создания собственных команд и окружения

Краткая таблица с основными типами аргументов и их описанием:

| Ключ | Краткое описание                                                                 |
|------|----------------------------------------------------------------------------------|
| **m**  | Обычный обязательный аргумент (один токен или `{...}`).                          |
| **r**  | Обязательный аргумент с разделителями (формат: `r⟨t1⟩⟨t2⟩`).                     |
| **R**  | Как `r`, но с значением по умолчанию (формат: `R⟨t1⟩⟨t2⟩{⟨default⟩}`).          |
| **v**  | Дословный аргумент (аналог `\verb` в LaTeX).                                   |
| **b**  | Тело окружения (только для `\begin{...}...\end{...}`).                         |
| **o**  | Необязательный аргумент в `[...]`, возвращает `-NoValue-` если отсутствует.     |
| **d**  | Необязательный аргумент с разделителями (формат: `d⟨t1⟩⟨t2⟩`).                 |
| **O**  | Как `o`, но с значением по умолчанию (формат: `O{⟨default⟩}`).                 |
| **D**  | Как `d`, но с значением по умолчанию (формат: `D⟨t1⟩⟨t2⟩{⟨default⟩}`).         |
| **s**  | Необязательная звезда (`*`), возвращает `\BooleanTrue`/`\BooleanFalse`.        |
| **t**  | Необязательный токен (формат: `t⟨token⟩`), возвращает `\BooleanTrue`/`False`.  |
| **e**  | Необязательные "украшения" (формат: `e{⟨tokens⟩}`), возвращает `-NoValue-`.    |
| **E**  | Как `e`, но с значениями по умолчанию (формат: `E{⟨tokens⟩}{⟨defaults⟩}`).     |
| **+**  | Модификатор: делает аргумент "длинным" (параграфным).                           |
| **!**  | Модификатор: управляет пробелами перед необязательными аргументами.             |
| **=**  | Модификатор: аргумент интерпретируется как ключ-значение (keyvals).             |
| **>**  | Модификатор: применяет "процессор аргументов" для преобразования ввода.        |

Создание команд документа

\NewDocumentCommand {⟨cmd⟩} {⟨arg spec⟩} {⟨code⟩}
\RenewDocumentCommand {⟨cmd⟩} {⟨arg spec⟩} {⟨code⟩}
\ProvideDocumentCommand {⟨cmd⟩} {⟨arg spec⟩} {⟨code⟩}
\DeclareDocumentCommand {⟨cmd⟩} {⟨arg spec⟩} {⟨code⟩}

Эта группа команд используется для создания команды ⟨cmd⟩. Спецификация аргумента для функции задается с помощью ⟨arg spec⟩, а команда использует ⟨code⟩, где #1, #2 и т. д. заменяются на аргументы, найденные парсером.

Пример:

\NewDocumentCommand\chapter{s o m} {
  \IfBooleanTF{#1}
    {\typesetstarchapter{#3}}
    {\typesetnormalchapter{#2}{#3}}
}

Создание окружений документа

\NewDocumentEnvironment {⟨env⟩} {⟨arg spec⟩} {⟨beg-code⟩} {⟨end-code⟩}
\RenewDocumentEnvironment {⟨env⟩} {⟨arg spec⟩} {⟨beg-code⟩} {⟨end-code⟩}
\ProvideDocumentEnvironment {⟨env⟩} {⟨arg spec⟩} {⟨beg-code⟩} {⟨end-code⟩}
\DeclareDocumentEnvironment {⟨env⟩} {⟨arg spec⟩} {⟨beg-code⟩} {⟨end-code⟩}

Эти команды работают так же, как \NewDocumentCommand и т. д., но создают окружения (\begin{⟨env⟩} … \end{⟨env⟩}). Как ⟨beg-code⟩, так и ⟨end-code⟩ могут получать доступ к аргументам, как определено в ⟨arg spec⟩. Аргументы будут переданы после \begin{⟨env⟩}. Все пробелы в начале и в конце {⟨env⟩} удаляются перед определением, таким образом:

Проверка специальных значений

\IfNoValueTF {⟨arg⟩} {⟨true code⟩} {⟨false code⟩}
\IfNoValueT {⟨arg⟩} {⟨true code⟩}
\IfNoValueF {⟨arg⟩} {⟨false code⟩}

Команды \IfNoValue(TF) используются для проверки, является ли ⟨argument⟩ (#1, #2 и т. д.) специальным маркером -NoValue-. Например:

\NewDocumentCommand\foo{o m} {
  \IfNoValueTF {#1}
    {\DoSomethingJustWithMandatoryArgument{#2}}
    {\DoSomethingWithBothArguments{#1}{#2}}
}
\IfValueTF {⟨arg⟩} {⟨true code⟩} {⟨false code⟩}
\IfValueT {⟨arg⟩} {⟨true code⟩}
\IfValueF {⟨arg⟩} {⟨false code⟩}

Обратная форма тестов \IfNoValue(TF) также доступна как \IfValue(TF). Контекст определит, какая логическая форма имеет наибольший смысл для данного сценария кода.

\IfBlankTF {⟨arg⟩} {⟨true code⟩} {⟨false code⟩}
\IfBlankT {⟨arg⟩} {⟨true code⟩}
\IfBlankF {⟨arg⟩} {⟨false code⟩}

Таблица описывающая команды проверки специальных значений:

| Команда                     | Краткое описание                                                                 |
|-----------------------------|----------------------------------------------------------------------------------|
| `\BooleanFalse`             | Логическое значение "ложь" (используется с аргументами `s`/`t`).                |
| `\BooleanTrue`              | Логическое значение "истина" (используется с аргументами `s`/`t`).              |
| `\IfBooleanTF{arg}{true}{false}` | Проверяет, является ли `arg` булевым значением (`\BooleanTrue`/`\BooleanFalse`). |
| `\IfBooleanT{arg}{true}`    | Выполняет `true` код, если `arg` равно `\BooleanTrue`.                          |
| `\IfBooleanF{arg}{false}`   | Выполняет `false` код, если `arg` равно `\BooleanFalse`.                        |
| `\IfNoValueTF{arg}{true}{false}` | Проверяет, равен ли `arg` маркеру `-NoValue-`.                                 |
| `\IfNoValueT{arg}{true}`    | Выполняет `true` код, если `arg` равен `-NoValue-`.                             |
| `\IfNoValueF{arg}{false}`   | Выполняет `false` код, если `arg` **не** равен `-NoValue-`.                     |
| `\IfValueTF{arg}{true}{false}` | Обратная логика: `true` если `arg` **имеет** значение.                         |
| `\IfValueT{arg}{true}`      | Выполняет `true` код, если `arg` **имеет** значение.                            |
| `\IfValueF{arg}{false}`     | Выполняет `false` код, если `arg` **не имеет** значения.                        |
| `\IfBlankTF{arg}{true}{false}` | Проверяет, является ли `arg` пустым или содержит только пробелы.               |
| `\IfBlankT{arg}{true}`      | Выполняет `true` код, если `arg` пустой/пробельный.                            |
| `\IfBlankF{arg}{false}`     | Выполняет `false` код, если `arg` **не** пустой.                               |

Пример использования:

\NewDocumentCommand\foo{o m}{
  \IfNoValueTF{#1}
    {Команда без опции: #2}
    {Команда с опцией [#1] и аргументом #2}
}

Таблица с описанием процессоров аргументов и примерами их использования:

| Процессор                     | Описание                                                                 | Пример использования                                                                 |
|--------------------------------|--------------------------------------------------------------------------|--------------------------------------------------------------------------------------|
| **`\SplitArgument{n}{sep}`**   | Разделяет аргумент по `sep` на `n+1` частей, оборачивая в `{}`.          | `\NewDocumentCommand\foo{>{\SplitArgument{2}{;}}m}{#1}``\foo{a;b;c}``{a}{b}{c}` |
| **`\SplitList{sep}`**          | Разделяет аргумент по `sep` на неограниченное число частей.              | `\NewDocumentCommand\foo{>{\SplitList{,}}m}{\ProcessList{#1}{\textbf}}``\foo{a,b}`**a** **b** |
| **`\ProcessList{list}{cmd}`**  | Применяет `cmd` к каждому элементу `list` (используется с `\SplitList`). | `\ProcessList{{a}{b}}{\textbf}`**a****b**                                         |
| **`\ReverseBoolean`**          | Инвертирует булевы значения (`\BooleanTrue``\BooleanFalse`).          | `\NewDocumentCommand\foo{>{\ReverseBoolean}s}{Star: \IfBooleanT{#1}{YES}}``\foo*` выведет "Star: NO" |
| **`\TrimSpaces`**              | Удаляет пробелы в начале/конце аргумента.                                | `\NewDocumentCommand\foo{>{\TrimSpaces}m}{[#1]}``\foo{  text  }``[text]`       |
| **Комбинация процессоров**     | Процессоры применяются справа налево.                                    | `>{\TrimSpaces} >{\SplitList{,}} m` → сначала разделит запятыми, затем обрежет пробелы. |

Примеры использования

  1. Разделение аргументов с обработкой:

    \NewDocumentCommand\formatlist{>{\SplitList{,}}m}{
      \ProcessList{#1}{\textbf}
    }
    \formatlist{a, b, c} % → **a** **b** **c**
    
  2. Обработка ключевых значений:

    \NewDocumentCommand\setoptions{>{\TrimSpaces}m}{
      \keys_set:nn {my-module} {#1}
    }
    \setoptions{ key1 = value1 , key2 = value2 }
    
  3. Инверсия логики звездочки:

    \NewDocumentCommand\checkstar{>{\ReverseBoolean}s}{
      \IfBooleanT{#1}{Звезды НЕТ} \IfBooleanF{#1}{Звезда ЕСТЬ}
    }
    \checkstar* % Выведет "Звезда ЕСТЬ"
    
  4. Многоэтапная обработка:

    \NewDocumentCommand\process{>{\TrimSpaces} >{\SplitList{;}} m}{
      \ProcessList{#1}{\SomeCommand}
    }
    \process{ a; b; c } % → обработает как `{a}{b}{c}`