Что такое команда \span?

Что такое команда \span?

\spanЯ пару раз натыкался на примитив. У меня сложилось впечатление, что он определен в программе TeX, даже не в plain.tex. Это верно? И чтоделаетчто делает эта команда и для чего она?

решение1

Примитив \spanимеет два совершенно разных значения, независимо от того, появляется ли он в преамбуле \halignили в основной части.

Если он находится в преамбуле, то есть перед первым, \crто означает «расширить» следующий токен; если он находится в теле, то он заканчивает текущую ячейку, но объединяет ее со следующей. В этом случае он обычно находится в сочетании с \omit.

Самый простой пример:

\halign{\hfil#\tabskip1em&#\hfil\cr
  a&b\cr
  c\span d\cr
}

где вторая строка будет иметь только одну ячейку. Выход будет

введите описание изображения здесь

без \tabskipпробела между «c» и «d», поскольку вторая строка — это всего лишь одна ячейка с \hfilсодержимым до и после, но TeX учтет это \tabskipпри определении ширины ячейки.

Обычно, однако, также добавляют, \omitчтобы удалитьтыивчасти (тыотносится к тому, что было до этого #,вк тому, что следует за этим).

Когда TeX читает преамбулу, \halignон не расширяет токены, пока не найдет \tabskip(когда он расширяет токены для поиска соответствующей спецификации склеивания). Расширение токена можно принудительно выполнить, поставив перед ним \span.

Примером может служитьamsmath.sty

\def\align@#1#2{%
    \inalign@true \intertext@ \Let@ \chardef\dspbrk@context\z@
    \ifingather@\else\displ@y@\fi
    \let\math@cr@@@\math@cr@@@align
    \ifxxat@\else \let\tag\tag@in@align \fi
    \let\label\label@in@display
    #1% set st@r
    \ifst@rred\else \global\@eqnswtrue \fi
    \measure@{#2}%
    \global\row@\z@
    \tabskip\eqnshift@
    \halign\bgroup
        \span\align@preamble\crcr
        #2%
}

Это вспомогательный макрос, используемый для alignи друзей; преамбула определяется один раз и навсегда в макросе\align@preamble

\def\align@preamble{%
   &\hfil
    \strut@
    \setboxz@h{\@lign$\m@th\displaystyle{##}$}%
    \ifmeasuring@\savefieldlength@\fi
    \set@field
    \tabskip\z@skip
   &\setboxz@h{\@lign$\m@th\displaystyle{{}##}$}%
    \ifmeasuring@\savefieldlength@\fi
    \set@field
    \hfil
    \tabskip\alignsep@
}

Это значительно упрощает определение \align@, помещая большой кусок кода в макрос. Это также позволяет изменять поведение alignпутем модификации \align@preamble(см.Можно ли сделать так, чтобы нечетные столбцы имели неявный префикс {}?).

Ядро LaTeX использует другой метод tabular, поскольку ему необходимо построить преамбулу в соответствии со своими правилами, то есть комбинацию lcrpсимволов и т. д.

Второе использование for \span— для объединения столбцов в \halign. В Plain TeX мы находим\multispan

\def\multispan#1{\omit \mscount#1\relax
  \loop\ifnum\mscount>\@ne \sp@n\repeat}
\def\sp@n{\span\omit\advance\mscount\m@ne}

который делает \omitи добавляет столько \span\omitпар, сколько указано в аргументе (минус один); поэтому \multispan{1}эквивалентно \omit, \multispan{2}и \omit\span\omitтак далее. \multicolumnМакрос LaTeX построен на той же идее

% latex.ltx, line 5053:
\long\def\multicolumn#1#2#3{\multispan{#1}\begingroup
  \@mkpream{#2}%
  \def\@sharp{#3}\set@typeset@protect
  \let\@startpbox\@@startpbox\let\@endpbox\@@endpbox
  \@arstrut \@preamble\hbox{}\endgroup\ignorespaces}

Сначала он делает \multispan{#1}(определяется точно так же, как в Plain), а затем приступает к построению «локальной» преамбулы выравнивания, используя ту же, что \@mkpreamиспользуется tabularи arrayдля оценки их обязательного аргумента.

Почему Кнут использовал один и тот же примитив для двух совершенно разных значений? Чтобы сэкономить место; TeX был написан, когда памяти компьютера было мало, и экономия на определении была важна. Поскольку \spanможет появляться только в \halign(или \valign, конечно), это не проблема.

Неправильное использование \spanв первом значении появляется в решении упражнения 20.16 в TeXbook

Не стоит воспринимать следующее слишком серьезно, но это работает:

{\setbox0=\vbox{\halign{#{\c\span\d}\cr
       \let\next=0\edef\next#1{\gdef\next{\b#1}}\next\cr}}}
 \let\a=\next

Упражнение заключается в определении \aэквивалентности \b(полностью развернутому) с последующим \c(не развернутым) и \d(развернутым только один раз) без использования \noexpandи \the. Например, если у нас есть

\def\foo{xy\baz}
\def\baz{z}
\def\b{\foo\foo}
\def\c{--}
\def\d{\baz}

мы хотим определить, \aчтобы иметь в качестве заменяющего текста

xyzxyz\c\baz

Часть \setbox0=\vbox{просто используется \halignбез какого-либо вывода. Теперь TeX оценивает \halignпреамбулу как #{\c\baz}\cr, поскольку \spanвызывает одноуровневое расширение \d. Единственная ячейка содержит

\let\next=0\edef\next#1{\gdef\next{\b#1}}\next

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

\let\next=0\edef\next#1{\gdef\next{\b#1}}\next{\c\baz}\cr

Просто \crзаканчивает ячейку, поэтому она не имеет значения. Из-за \let\next=0, \nextстановится нерасширяемым, поэтому \edefвызывает \nextопределение, как если бы она была

\def\next#1{\gdef\next{<full expansion of \b>#1}}

и поэтому \next{\c\baz}будет добросовестно исполнять

\gdef\next{<full expansion of \b>\c\baz}

и финал \let\a=\nextкладет всему конец.

Это хорошо объясняет, что \spanделает толькоодиншаг расширения.


Другое решение с e-TeX (он ведь не использует \noexpandи ) было бы таким:\the

\edef\a{\b\unexpanded\expandafter{\expandafter\c\d}}

Связанный контент