Пытаясь изучить макросы в LaTeX, я наткнулся наэто определение:
\x@protect
макрос с одним аргументом, представленным как "#1
".\@typeset@protect
также определяется как и, следовательно, выполняется\relax
первая ветвь , которая ничего не делает. ( сравнивает значение двух макросов.) Таким образом, единственным результатом является то, что его аргумент, первый " " в определении , отбрасывается. Это оставляет команду \protect, которая является не-операцией, и себя самого. Это кажется циклическим определением. На самом деле, это не так. Это грязный трюк со стороны авторов LaTeX, пытающихся замести следы. Еще раз, крайне подозрительно, посмотрите на листинг . Между последним " " и точкой есть два пробела , тогда как после последней управляющей последовательности в других листингах есть только один! На самом деле, во всех листингах есть только один завершающий пробел. Предпоследний пробел в листинге является частью имени его последней управляющей последовательности, которая является " ", включая пробел!`\ifx
\ifx
\x@protect
\\
\\
\\
\\
\\
\\
\\
Почему циклическая ссылка?
Почему он не ломается при обнаружении циклической ссылки?
решение1
Давайте начнем интерактивный сеанс с pdflatex test
, где test.tex
находится
\documentclass{article}
\DeclareRobustCommand{\?}{js bibra}
\makeatletter
\show\?
Команда \show
остановит выполнение, и мы сможем выполнить больше команд.
This is pdfTeX, Version 3.14159265-2.6-1.40.20 (TeX Live 2019) (preloaded format=pdflatex)
restricted \write18 enabled.
entering extended mode
(./test.tex
LaTeX2e <2019-10-01> patch level 3
(/usr/local/texlive/2019/texmf-dist/tex/latex/base/article.cls
Document Class: article 2019/10/25 v1.4k Standard LaTeX document class
(/usr/local/texlive/2019/texmf-dist/tex/latex/base/size10.clo))
> \?=macro:
->\x@protect \?\protect \? .
l.7 \show\?
? i\show\x@protect
> \x@protect=macro:
#1->\ifx \protect \@typeset@protect \else \@x@protect #1\fi .
<insert> \show\x@protect
l.7 \show\?
? i\show\@x@protect
> \@x@protect=macro:
#1\fi #2#3->\fi \protect #1.
<insert> \show\@x@protect
l.7 \show\?
Что происходит, когда \?
обрабатывается? Есть два случая: если \protect
не имеет того же значения, что и \@typeset@protect
(который есть \relax
), то следует ложная ветвь. Таким образом, входной поток будет иметь
\@x@protect\?\fi\protect\?
а расширение \@x@protect
удалит последние два токена, оставив \protect\?\fi
(и \fi
в конечном итоге исчезнет).
Это происходит, например, в \protected@edef
или \protected@write
, когда \protect
присваивается иное значение, чем \@typeset@protect
.
Если это так, то условие истинно, поэтому входной поток будет иметь, после пропуска ветви false,
\protect\?
Теперь \protect
исчезает и мыказатьсябыть в том же месте, что и раньше. Но мы не там, потому что следующий токен \protect
- этодругойиз \?
этого был сделан ввод в тестовый документ.
Посмотрите внимательно на вывод первой \show
команды. Получаем
->\x@protect \?\protect \? .
Между ->
и завершающей точкой TeX представляет заменяющий текст макроса. Правила для этого представления таковы, что за управляющими словами следует пробел, тогда как за управляющими символами — нет. Это объясняет пробел после \x@protect
и , \protect
а также отсутствие пробела после \?
. Но завершающей точке предшествуетдвапробелы! Откуда они берутся?
Когда вы это делаете \DeclareRobustCommand{\?}{js bibra}
, LaTeX выполняет несколько действий, главное из которых — что-то вроде
\expandafter\def\csname ? \endcsname{js bibra}
и затем использование этого макроса с очень нестандартным именем для определения «версии уровня пользователя» \?
. Обратите внимание на пробел перед \endcsname
, который оказывается в имени макроса.
Есть еще некоторые детали, но идея в том, чтобы облегчить написание вспомогательных файлов. В старых версиях LaTeX мы видели что-то вроде
\def\LaTeX{\protect\pLaTeX}
\def\pLaTeX{<the real definition>}
Когда был выпущен LaTeX2e, предыдущий код стал
\DeclareRobustCommand{\LaTeX}{<the real definition>}
эксплуатируя новый уровень абстракции. В старых версиях \LaTeX{}
в заголовке раздела было бы написано
\protect\pLaTeX {}
во вспомогательных файлах. Теперь он выписывает
\LaTeX {}
из-за завершающего пробела в имени и правиле. Двойной пробел будет проигнорирован при чтении вспомогательного файла.
\\
Для управляющих символов, таких как или , это немного отличается \?
, но общая идея та же.