I have bumped into the \span
primitive a couple of time. I got the impression it is defined in the TeX program, not even in plain.tex
. Is that right? And what does that command do and what is it for?
The primitive \span
has two very different meanings if it appear in the preamble of \halign
or in the body.
If it's in the preamble, that is before the first \cr
it means “expand” the following token; if it appears in the body it ends the current cell, but merges it with the following one. In this case it's usually in combination with \omit
.
The simplest example is
\halign{\hfil#\tabskip1em&#\hfil\cr
a&b\cr
c\span d\cr
}
where the second row will have just one cell. The output will be
with no \tabskip
spacing between “c” and “d”, because the second row is just one cell with \hfil
before and after the contents, but TeX will take the \tabskip
into account for determining the cell's width.
Usually, however, one also adds \omit
in order to remove the u and v parts (u refers to what comes before #
, v to what follows it).
When TeX is reading a preamble for \halign
it doesn't expand tokens unless it finds \tabskip
(when it expands tokens for finding the appropriate glue specification). Expansion of a token can be forced by preceding it with \span
.
An example is in 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%
}
This is a helper macro used for align
and friends; the preamble is defined once and for all in the 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@
}
This greatly simplifies the definition of \align@
, by placing a big chunk of code in a macro. It also allows for changing the behavior of align
by modifying \align@preamble
(see Is it possible to make odd-numbered columns have implicitly a prefix {}?).
The LaTeX kernel uses a different method for tabular
, because it needs to build the preamble according to its rules, that is, a combination of lcrp
characters and so on.
The second usage for \span
is for merging columns in an \halign
. In Plain TeX we find \multispan
\def\multispan#1{\omit \mscount#1\relax
\loop\ifnum\mscount>\@ne \sp@n\repeat}
\def\sp@n{\span\omit\advance\mscount\m@ne}
that does \omit
and adds as many \span\omit
pairs as stated in the argument (minus one); so \multispan{1}
is equivalent to \omit
, \multispan{2}
to \omit\span\omit
and so on. The \multicolumn
macro of LaTeX is built upon the same 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}
It first does \multispan{#1}
(defined exactly as in Plain) and then proceeds to build a “local” alignment preamble using the same \@mkpream
used by tabular
and array
to evaluate their mandatory argument.
Why did Knuth use the same primitive for two very different meanings? In order to save space; TeX was written when computer memory space was low, and saving on a definition was important. Since \span
can only appear in \halign
(or \valign
, of course), it's not a problem.
A devious usage of \span
in the first meaning appears in the solution of exercise 20.16 in the TeXbook
The following shouldn't be taken too seriously, but it does work:
{\setbox0=\vbox{\halign{#{\c\span\d}\cr \let\next=0\edef\next#1{\gdef\next{\b#1}}\next\cr}}} \let\a=\next
The exercise is about defining \a
to be equivalent to \b
(fully expanded) followed by \c
(not expanded) and by \d
(expanded just once) without using \noexpand
and \the
. For instance, if we have
\def\foo{xy\baz}
\def\baz{z}
\def\b{\foo\foo}
\def\c{--}
\def\d{\baz}
we want to define \a
to have as its replacement text
xyzxyz\c\baz
The \setbox0=\vbox{
part is just to use \halign
without any output. Now TeX evaluates the \halign
preamble as #{\c\baz}\cr
, because \span
causes a one level expansion of \d
. The only cell contains
\let\next=0\edef\next#1{\gdef\next{\b#1}}\next
that, according to the rules, is used in place of #
in the preamble, so that the input stream becomes
\let\next=0\edef\next#1{\gdef\next{\b#1}}\next{\c\baz}\cr
The \cr
just ends the cell, so it's not relevant. Because of \let\next=0
, \next
becomes unexpandable, so \edef
causes \next
to be defined as if it had been
\def\next#1{\gdef\next{<full expansion of \b>#1}}
and so \next{\c\baz}
will dutifully execute
\gdef\next{<full expansion of \b>\c\baz}
and the final \let\a=\next
ends it all.
This explains well that \span
does only one step of expansion.
A different solution with e-TeX (it doesn't use \noexpand
and \the
, after all), would be
\edef\a{\b\unexpanded\expandafter{\expandafter\c\d}}
-
What is exactly happening in the solution of exercise 20.16? It would be nice to have an explanation. – Manuel Sep 27 '14 at 12:59
-
-
Great answer. Just one thing: I think you are missing a closed brace in
\def\next#1{\gdef\next{<full expansion of \b>#1}
, which should be\def\next#1{\gdef\next{<full expansion of \b>#1}}
. First closed brace for\next
's argument, second one ends definition. – MickG Sep 27 '14 at 13:47 -
-
\multicolumn
, like\span 3
columns :P – Manuel Sep 27 '14 at 12:30\multirow
. I remember reading it was used in\multi
something, but I don't remember if something was row or column :). It is also used in\sp@n
, which is LaTeX kernel. – MickG Sep 27 '14 at 12:33\multicolumn
->\multispan
->\@multispan
->\sp@n
->\span
.\multirow
doesn't use it. – MickG Sep 27 '14 at 12:36