Me he topado con lo \span
primitivo un par de veces. Me dio la impresión de que está definido en el programa TeX, ni siquiera enplain.tex
. ¿Está bien? Y quéhaceque comando hace y para que sirve?
Respuesta1
El primitivo \span
tiene dos significados muy diferentes si aparece en el preámbulo \halign
o en el cuerpo.
Si está en el preámbulo, es decir, antes del primero, \cr
significa "expandir" el siguiente token; si aparece en el cuerpo finaliza la celda actual, pero la fusiona con la siguiente. En este caso suele ir en combinación con \omit
.
El ejemplo más simple es
\halign{\hfil#\tabskip1em&#\hfil\cr
a&b\cr
c\span d\cr
}
donde la segunda fila tendrá solo una celda. La salida será
sin \tabskip
espacio entre “c” y “d”, porque la segunda fila es solo una celda con \hfil
antes y después del contenido, pero TeX tendrá en \tabskip
cuenta para determinar el ancho de la celda.
Sin embargo, normalmente también se añade \omit
para eliminar eltuyvpartes (tuse refiere a lo que viene antes #
,va lo que sigue).
Cuando TeX lee un preámbulo, \halign
no expande los tokens a menos que los encuentre \tabskip
(cuando expande los tokens para encontrar la especificación de pegamento adecuada). La expansión de un token se puede forzar precediéndolo con \span
.
Un ejemplo está enamsmath.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 es una macro auxiliar utilizada para align
y amigos; el preámbulo se define de una vez por todas en la 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@
}
Esto simplifica enormemente la definición de \align@
, al colocar una gran cantidad de código en una macro. También permite cambiar el comportamiento align
modificando \align@preamble
(ver¿Es posible hacer que las columnas impares tengan implícitamente un prefijo {}?).
El kernel de LaTeX utiliza un método diferente para tabular
, porque necesita construir el preámbulo de acuerdo con sus reglas, es decir, una combinación de lcrp
caracteres, etc.
El segundo uso de \span
es para fusionar columnas en un archivo \halign
. En 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}
que hace \omit
y suma tantos \span\omit
pares como se indica en el argumento (menos uno); so \multispan{1}
es equivalente a \omit
, \multispan{2}
to \omit\span\omit
y así sucesivamente. La \multicolumn
macro de LaTeX se basa en la misma idea.
% 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}
Primero lo hace \multispan{#1}
(definido exactamente como en Plain) y luego procede a construir un preámbulo de alineación "local" utilizando el mismo \@mkpream
utilizado por tabular
y array
para evaluar su argumento obligatorio.
¿Por qué Knuth utilizó el mismo primitivo para dos significados muy diferentes? Para ahorrar espacio; TeX se escribió cuando el espacio en la memoria de la computadora era bajo y era importante guardar una definición. Como \span
sólo puede aparecer en \halign
(o \valign
, por supuesto), no es un problema.
Un uso tortuoso de \span
en el primer significado aparece en la solución del ejercicio 20.16 del TeXbook.
Lo siguiente no debe tomarse demasiado en serio, pero funciona:
{\setbox0=\vbox{\halign{#{\c\span\d}\cr \let\next=0\edef\next#1{\gdef\next{\b#1}}\next\cr}}} \let\a=\next
El ejercicio consiste en definir \a
que sea equivalente a \b
(completamente expandido) seguido de \c
(no expandido) y de \d
(expandido solo una vez) sin usar \noexpand
y \the
. Por ejemplo, si tenemos
\def\foo{xy\baz}
\def\baz{z}
\def\b{\foo\foo}
\def\c{--}
\def\d{\baz}
queremos definir \a
tener como texto de reemplazo
xyzxyz\c\baz
La \setbox0=\vbox{
pieza es solo para usar \halign
sin ningún resultado. Ahora TeX evalúa el \halign
preámbulo como #{\c\baz}\cr
, porque \span
provoca una expansión de un nivel de \d
. La única celda contiene
\let\next=0\edef\next#1{\gdef\next{\b#1}}\next
que, de acuerdo con las reglas, se usa en lugar de #
en el preámbulo, de modo que el flujo de entrada se convierte en
\let\next=0\edef\next#1{\gdef\next{\b#1}}\next{\c\baz}\cr
Simplemente \cr
termina la celda, por lo que no es relevante. Debido a que \let\next=0
se \next
vuelve inexpandible, por lo que \edef
provoca \next
que se defina como si hubiera sido
\def\next#1{\gdef\next{<full expansion of \b>#1}}
y así \next{\c\baz}
ejecutará obedientemente
\gdef\next{<full expansion of \b>\c\baz}
y el final \let\a=\next
acaba con todo.
Esto explica bien que \span
solounopaso de expansión.
Una solución diferente con e-TeX (no usa \noexpand
y \the
, después de todo), sería
\edef\a{\b\unexpanded\expandafter{\expandafter\c\d}}