命令 \span 是什么?

命令 \span 是什么?

\span我曾多次碰到过这个原语。我感觉它是在 TeX 程序中定义的,甚至不在 中plain.tex。对吗?那是什么该命令的作用是什么?

答案1

\span如果原语出现在序言中\halign或正文中,则它有两种截然不同的含义。

如果它在前导码中,即在第一个之前,\cr则表示“扩展”以下标记;如果它出现在正文中,则它会结束当前单元格,但会将其与下一个单元格合并。在这种情况下,它通常与 结合使用\omit

最简单的例子是

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

第二行只有一个单元格。输出将是

在此处输入图片描述

“c” 和 “d” 之间没有\tabskip间距,因为第二行只是一个单元格,其\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字符组合等等。

的第二种用法\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相同的对(减一);所以相当于,到等等。LaTeX的宏建立在同样的思想之上\span\omit\multispan{1}\omit\multispan{2}\omit\span\omit\multicolumn

% 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 中的定义完全相同),然后继续构建“本地”对齐前导码,使用和评估其强制参数相同\@mkpreamtabular方法array

为什么 Knuth 会用相同的原语来表示两种截然不同的含义?为了节省空间;TeX 是在计算机内存空间不足时编写的,节省定义很重要。由于\span只能出现在\halign(或\valign,当然),所以这不是问题。

第一个含义的不正当用法\span出现在 TeXbook 练习 20.16 的解决方案中

下面的做法不必太当真,但确实有效:

{\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}}

相关内容