
Тесно связано:Как реализовать \expandbefore, аналогично \expandafter?
В связанном вопросе делается ссылка на "# Notation"
. Как работает синтаксис?
Из ответа на другой вопрос:
\makeatletter
%
\newcommand\name{}%
\long\def\name#1#{\UD@innername{#1}}%
%
\newcommand\UD@innername[2]{%
\expandafter\UD@exchange\expandafter{\csname#2\endcsname}{#1}%
}%
%
\newcommand\UD@exchange[2]{#2#1}%
%
\makeatother
Как на самом деле работает синтаксис параметра (без числа)? \def\test#1#{\csname test#1\endcsname}? Каковы хорошие варианты использования этого шаблона?
решение1
Суть такова \name
:
С
\name⟨stuff without braces before the left-brace⟩{⟨stuff within braces⟩}
TeX будет в качестве первого аргумента извлекать ⟨stuff without braces before the left-brace⟩
, а в качестве второго аргумента обрабатывать ⟨stuff within braces⟩
:
Используется ⟨stuff within braces⟩
для имени токена управляющей последовательности, который может быть получен путем включения ⟨stuff within braces⟩
в \csname..\endcsname
результирующее выражение и его выполнения \csname..\endcsname
.
Вместо этого control-sequence-token будет позади .{⟨stuff within braces⟩}
⟨stuff without braces before the left-brace⟩
При выполнении макросов с разделенными аргументами (La)TeX собирает токены, формирующие аргументы, из потока токенов и при этом в потоке токенов ищет разделители аргументов, поскольку разделители аргументов принимаются за маркеры для завершения процесса сбора токенов для рассматриваемого аргумента.
Какие разделители следует искать, должно быть указано в⟨текст параметра⟩определения.
Вы можете обозначить, что (La)TeX будет искать разделитель для последнего аргумента, например, a, \relax
записав \relax
в качестве последнего элемента⟨текст параметра⟩.
Вы можете обозначить это как разделитель для последнего аргумента (La)TeX будет искать левую фигурную скобку, написав #
в качестве последнего элемента⟨текст параметра⟩.
В (почти ;-) ) любом случае⟨текст параметра⟩макроопределения следует⟨текст замены⟩, вложенные в фигурные скобки.
Таким образом, в (почти ;-) ) любом случае⟨текст параметра⟩макроопределения будет заканчиваться левой фигурной скобкой пары фигурных скобок, которые окружают⟨текст замены⟩.
Таким образом, когда последний токен⟨текст параметра⟩является #
, то он будет заканчиваться {
.
Вот почему эта штука для обозначения того, что (La)TeX будет искать левую фигурную скобку в качестве разделителя при сборе последнего аргумента для макроса, также называется #{
-нотацией.
Тонкое различие между поиском \relax
и поиском открывающей скобки заключается в следующем:
Когда (La)TeX в качестве разделителя для аргумента макроса, например, ищет \relax
и находит его, он не только прекращает сбор токенов для аргумента, но и удаляет разделитель/токен \relax
.
Когда (La)TeX в качестве разделителя для последнего аргумента макроса ищет левую фигурную скобку и находит ее, он прекращает сбор токенов для последнего аргумента и оставляет этот разделитель/эту фигурную скобку на месте и помещает макрос⟨текст замены⟩перед этим.
Цитаты из книги TeXbook профессора Дональда Эрвина Кнута, Глава 20: Определения (также называемые макросами) — профессор Дональд Эрвин Кнут,почетный профессор искусства программирования в Стэнфордском университете, является изобретателем TeX:
Теперь, когда мы увидели несколько примеров, давайте рассмотрим точные правила, которые управляют макросами TeX. Определения имеют общую форму
\def⟨управляющая последовательность⟩⟨текст параметра⟩{⟨текст замены⟩}
где⟨текст параметра⟩не содержит фигурных скобок, и где все вхождения{и }в⟨текст замены⟩правильно вложены. Кроме того, символ # имеет особое значение: в ⟨текст параметра⟩, за первым появлением # должна следовать цифра 1, за следующим — цифра 2 и т. д.; допускается до девяти символов #. В⟨текст замены⟩ за каждым # должна следовать цифра, которая появилась после # в⟨текст параметра⟩, или за # должен следовать еще один #. Последний случай означает один токен # при расширении макроса; первый случай означает вставку соответствующего аргумента.
[...]
К этим правилам допускается специальное расширение: если самый последний символ⟨текст параметра⟩это #, так что за этим # сразу следует {, TeX будет вести себя так, как будто{были вставлены в правый конец как текста параметра, так и текста замены. Например, если вы скажете
'\def\а#1#{\hbox к #1}'
, последующий текст '\a3pt{x}' расширится до '\hbox to 3pt{x}', поскольку аргумент \a отделен левой фигурной скобкой.
Другими словами:
Если вы скажете , последующий текст будет обработан следующим образом:\def\a#1#{\hbox to #1}
\a3pt{x}
В качестве аргумента для макроса \a
TeX из последующего текста соберет аргумент, который отделен левой фигурной скобкой: Он соберет фразу 3pt
.
Левая фигурная скобка (и все, что за ней) останется на месте при замене \a
и ее аргумента⟨текст замены⟩дает: \hbox to 3pt
так что все будет: \hbox to 3pt{x}
.
Короче говоря: #{
-нотация (обратите внимание на левую фигурную скобку!) означает, что последний параметр рассматриваемого макроса отделяется левой фигурной скобкой (с кодом категории 1 (начало группы)), которая останется на месте, когда (La)TeX извлечет из потока токенов последний аргумент для этого макроса.
Тот факт, что в этом особом случае разделитель, т. е. левая фигурная скобка, останется на месте, примечателен, поскольку это единственная ситуация, когда разделитель аргумента не будет удален из потока токенов в процессе сбора аргумента.
Т.е., если разделитель аргументов будет, например, токеном, \relax
как в
\def\mymacro#1\relax{The Argument#1 was delimited by relax. }
, \relax
токен, служащий разделителем, будет удален при сборе аргумента из потока токенов: С
\mymacro , which is nonsense,\relax...
в качестве аргумента для \mymacro
(La)TeX соберет из потока токенов последовательность , which is nonsense,
, а затем найдет токен \relax
и примет этот \relax
токен за разделитель аргумента, и поэтому (La)TeX прекратит собирать токены для аргумента и удалит разделитель. (Затем он начнет собирать другие аргументы, если \mymacro
в соответствии с⟨текст параметра⟩его определения были. Но их нет.) Тогда он бы доставил⟨текст замены⟩:
The Argument, which is nonsense, was delimited by relax.
Этот⟨текст замены⟩в потоке токенов будут заканчиваться тремя точками, и поэтому теперь поток токенов будет содержать:
The Argument, which is nonsense, was delimited by relax. ...
Но если определение такое:
\def\mymacro#1#{The Argument#1 was delimited by a left-brace. }
и вы говорите
\mymacro , which is nonsense,{...
, ты получишь
The Argument, which is nonsense, was delimited by a left-brace. {...
потому что в отличие от предыдущего случая, где \relax
удаляется скобка, ограничивающая аргумент, в этом случае левая фигурная скобка, ограничивающая аргумент, не будет удалена.
Фраза
Если самый последний символ⟨текст параметра⟩это #, так что за этим # сразу следует {, TeX будет вести себя так, как будто{был вставлен в правый конец как текста параметра, так и текста замены.
означает:
Определение обычно имеет шаблон
\def⟨управляющая последовательность⟩⟨текст параметра⟩{⟨текст замены⟩}
Предположим, вы хотите определить макрос \macro
, который обрабатывает два аргумента, из которых первый аргумент не разделен, а второй разделен последовательностью, \foo\bar
и который просто «выплевывает» аргументы.
В этом случае вы создаете выражение вроде:
\def\macro#1#2\foo\bar{argument 1: #1 argument 2: #2}
⟨управляющая последовательность⟩"="\macro
⟨текст параметра⟩"="#1#2\foo\bar
⟨текст замены⟩"="argument 1: #1 argument 2: #2
Когда вы смотрите на выражение, вы видите, чтов правом конце⟨текст параметра⟩, то будут присутствовать токены, образующие разделитель последнего аргумента, т.е. жетоны \foo\bar
. Они принадлежат к жетонам, которые образуют⟨текст параметра⟩. Прямо за ними вы можете увидеть левую скобку из той пары скобок, которая окружает⟨текст замены⟩.
Таким образом, в этом случае на правом конце⟨текст параметра⟩вы находите токены, которые ограничивают последний аргумент, и вы находите левую фигурную скобку пары фигурных скобок, которая окружает⟨текст замены⟩.
Последовательность
\macro{A}B\foo\bar
урожайность:
argument 1: A argument 2: B
Как видите, \foo\bar
при сборе токенов, принадлежащих аргументам, были удалены разделительные токены.
Если вместо этого вы определите:
\def\macro#1#2\foo\bar{argument 1: #1 argument 2: #2\foo\bar}
⟨управляющая последовательность⟩"="\macro
⟨текст параметра⟩"="#1#2\foo\bar
⟨текст замены⟩"="argument 1: #1 argument 2: #2\foo\bar
,т.е. если вы вставите разделитель \foo\bar
в правый конец⟨текст замены⟩также, последовательность
\macro{A}B\foo\bar
урожайность
argument 1: A argument 2: B\foo\bar
как будто в предыдущем определении разделитель остался на месте.
А что, если вы хотите определить что-то подобное, но разделителем будет не последовательность \foo\bar
, а левая фигурная скобка?
Определение все равно должно быть шаблонным
\def\macro#1#2⟨delimiter⟩{argument 1: #1 argument 2: #2⟨delimiter⟩}
но по нескольким причинам вы не можете принять {
это как⟨разделитель⟩и написать
\def\macro#1#2{{argument 1: #1 argument 2: #2{}
и настоящим принимаем #1#2{
для⟨текст параметра⟩и argument 1: #1 argument 2: #2{
для⟨текст замены⟩:
Во многих ситуациях невозможно легко вставить одинарные левые фигурные скобки, не получив ошибок из-за несбалансированности фигурных скобок.
Ситуация была бы неоднозначной, поскольку (La)TeX пришлось бы угадывать, находится ли открывающая скобка рядом/в правом конце⟨текст параметра⟩является ли он разделителем аргументов или принадлежит к той паре фигурных скобок, которая окружает⟨текст замены⟩.
(La)TeX также должен будет угадать, должна ли правая фигурная скобка просто совпадать с левой фигурной скобкой в конце⟨текст замены⟩или это должно обозначать конец⟨текст замены⟩.
Поэтому в качестве синтаксического обходного пути #{
была придумана нотация:
С
\def\macro#1#2#{argument 1: #1 argument 2: #2}
⟨управляющая последовательность⟩"= \macro
"
⟨текст параметра⟩"="#1#2#
⟨текст замены⟩"="argument 1: #1 argument 2: #2
,⟨текст параметра⟩не заканчивается токеном {
, который должен ограничивать последний аргумент, но заканчивается на #
.
Это #
используется для обозначения того, что (La)TeX будет искать левую фигурную скобку как разделитель аргументов, когда во время процесса расширения \macro
собирает токены последнего аргумента \macro
из потока токенов. Это #
также означает, что левая фигурная скобка, найденная как разделитель, должна быть оставлена на месте, так что⟨текст замены⟩из \macro
идет перед ним.
Другими словами:
Это #
заставляет (La)TeX вести себя так, как будто в качестве последнего токена, который должен разграничивать последний аргумент, была найдена левая фигурная скобка.⟨текст параметра⟩.
Это также заставляет (La)TeX вести себя так, как будто последний токен, указанный в определении,⟨текст замены⟩была левая фигурная скобка.
К вашему комментарию:
Эмм. Не думаю, что вы могли бы что-то придумать? Лучшее предположение о том, что происходит? Позвольте мне перефразировать, в вашем другом примере, если у вас вообще есть символы cat code 1, плавающие во всем этом. Правильно ли делать вывод, что это «примитивное поведение» происходит во время входной фазы чтения файла? (Извините, я придумываю терминологию по ходу дела ... Я говорю об
process_input_buffer
обработчике событий.
Самым первым этапом обработки в LaTeX является чтение входных данных (из файла или с консоли) и их обработка в качестве набора инструкций для помещенияжетоныв поток токенов. (Токены-символов/токены-последовательности-управления). На последующих этапах токены в потоке токенов обрабатываются. На более раннем из этих этапов расширяемые токены, например, макросы, расширяются. То есть они заменяются теми токенами, которые формируют тексты-замены их определений. На этом этапе происходит сбор токенов для макро-аргументов. Таким образом, поведение не происходит при чтении файла, но происходит после чтения и токенизации, на этапе расширения расширяемых токенов и тем самым сбора другихжетоныв качестве своих аргументов.
Я попытался подробно рассказать о том, как (La)TeX собирает и обрабатывает макроаргументы в своем ответе на вопросКак TeX ищет разделенные аргументы?
Я попытался подробно остановиться на этом \expandafter
и на способах, как этого избежать, в своем ответе на вопросКак узнать количество Expandafters при добавлении к макросу csname?
Я попытался подробно рассказать о макросе \name
в своем ответе на вопросОпределите последовательность управления, после которой пробел имеет значение.