我已經遇過\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
為了刪除你和v部分 (你指的是之前發生的事情#
,v到其後的內容)。
當 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
並添加\span\omit
與參數中所述一樣多的對(減去一個); so\multispan{1}
相當於\omit
, \multispan{2}
to\omit\span\omit
等等。 LaTeX 的巨集\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}
它首先執行(與 Plain 中的定義完全相同),然後繼續使用和 所使用的\multispan{#1}
相同內容建立「本地」對齊前導碼,以評估其強制參數。\@mkpream
tabular
array
為什麼高德納使用同一個原語來表達兩個截然不同的意義?為了節省空間; 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
(未擴展)和 by \d
(僅擴展一次)而不使用\noexpand
和\the
。例如,如果我們有
\def\foo{xy\baz}
\def\baz{z}
\def\b{\foo\foo}
\def\c{--}
\def\d{\baz}
我們想要定義\a
has 作為其替換文本
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
and )將是\the
\edef\a{\b\unexpanded\expandafter{\expandafter\c\d}}