Eu encontrei o \span
primitivo algumas vezes. Tive a impressão de que está definido no programa TeX, nem mesmo no plain.tex
. Isso está certo? E o quefazesse comando faz e para que serve?
Responder1
O primitivo \span
tem dois significados muito diferentes se aparecer no preâmbulo \halign
ou no corpo.
Se estiver no preâmbulo, é antes do primeiro \cr
significa “expandir” o token seguinte; se aparecer no corpo encerra a célula atual, mas a mescla com a seguinte. Neste caso, geralmente é combinado com \omit
.
O exemplo mais simples é
\halign{\hfil#\tabskip1em&#\hfil\cr
a&b\cr
c\span d\cr
}
onde a segunda linha terá apenas uma célula. A saída será
sem \tabskip
espaçamento entre “c” e “d”, pois a segunda linha é apenas uma célula com \hfil
antes e depois do conteúdo, mas o TeX levará isso \tabskip
em consideração para determinar a largura da célula.
Geralmente, porém, acrescenta-se também \omit
para remover ovocêevpeças (vocêrefere-se ao que vem antes #
,vpara o que se segue).
Quando o TeX está lendo um preâmbulo, \halign
ele não expande os tokens, a menos que encontre \tabskip
(quando expande os tokens para encontrar a especificação de cola apropriada). A expansão de um token pode ser forçada precedendo-o com \span
.
Um exemplo está emamsmath.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%
}
Esta é uma macro auxiliar usada para align
amigos; o preâmbulo é definido de uma vez por todas na macro\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@
}
Isso simplifica muito a definição de \align@
, colocando um grande pedaço de código em uma macro. Também permite alterar o comportamento align
modificando \align@preamble
(verÉ possível fazer com que colunas ímpares tenham implicitamente um prefixo {}?).
O kernel do LaTeX utiliza um método diferente para tabular
, pois precisa construir o preâmbulo de acordo com suas regras, ou seja, uma combinação de lcrp
caracteres e assim por diante.
O segundo uso de \span
é para mesclar colunas em um arquivo \halign
. No Plain TeX encontramos\multispan
\def\multispan#1{\omit \mscount#1\relax
\loop\ifnum\mscount>\@ne \sp@n\repeat}
\def\sp@n{\span\omit\advance\mscount\m@ne}
isso faz \omit
e adiciona tantos \span\omit
pares quanto indicado no argumento (menos um); so \multispan{1}
é equivalente a \omit
, \multispan{2}
a \omit\span\omit
e assim por diante. A \multicolumn
macro do LaTeX é construída sobre a mesma ideia
% 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}
Ele primeiro o faz \multispan{#1}
(definido exatamente como em Plain) e então prossegue para construir um preâmbulo de alinhamento “local” usando o mesmo \@mkpream
usado por tabular
e array
para avaliar seu argumento obrigatório.
Por que Knuth usou o mesmo primitivo para dois significados muito diferentes? Para economizar espaço; O TeX foi escrito quando o espaço de memória do computador era baixo e era importante economizar em uma definição. Como \span
só pode aparecer em \halign
(ou \valign
, é claro), não há problema.
Um uso tortuoso de \span
no primeiro significado aparece na solução do exercício 20.16 no TeXbook
O seguinte não deve ser levado muito a sério, mas funciona:
{\setbox0=\vbox{\halign{#{\c\span\d}\cr \let\next=0\edef\next#1{\gdef\next{\b#1}}\next\cr}}} \let\a=\next
O exercício trata de definir \a
como equivalente a \b
(totalmente expandido) seguido por \c
(não expandido) e por \d
(expandido apenas uma vez) sem usar \noexpand
e \the
. Por exemplo, se tivermos
\def\foo{xy\baz}
\def\baz{z}
\def\b{\foo\foo}
\def\c{--}
\def\d{\baz}
queremos definir \a
para ter como texto substituto
xyzxyz\c\baz
A \setbox0=\vbox{
parte é apenas para usar \halign
sem qualquer saída. Agora o TeX avalia o \halign
preâmbulo como #{\c\baz}\cr
, porque \span
causa uma expansão de um nível de \d
. A única célula contém
\let\next=0\edef\next#1{\gdef\next{\b#1}}\next
que, de acordo com as regras, é usado no lugar #
do preâmbulo, para que o fluxo de entrada se torne
\let\next=0\edef\next#1{\gdef\next{\b#1}}\next{\c\baz}\cr
O \cr
apenas termina a célula, então não é relevante. Por causa de \let\next=0
, \next
torna-se inexpansível, então \edef
faz com \next
que seja definido como se tivesse sido
\def\next#1{\gdef\next{<full expansion of \b>#1}}
e assim \next{\c\baz}
executará obedientemente
\gdef\next{<full expansion of \b>\c\baz}
e o final \let\a=\next
acaba com tudo.
Isso explica bem o que \span
faz apenasumpasso de expansão.
Uma solução diferente com o e-TeX (afinal, ele não usa \noexpand
e \the
), seria
\edef\a{\b\unexpanded\expandafter{\expandafter\c\d}}