背景
我目前正在做一些工作collcell
tabular
包收集/单元的内容array
并将其提供给用户定义的宏,例如使用>{\collectcell\mymacro}c<{\endcollectcell}
。
问题
我发现每个单元格的第一个标记在插入代码之前都会被扩展>{...}
。我不确定这是如何以及在哪里完成的。有人知道吗:
- 什么扩展了这个标记(即
array
/tabular
LaTeX 代码或 plainTeX\halign
代码或其他?)以及在哪里 - 为什么这样做(我有一些关于工资的想法,但我对“官方”原因非常感兴趣)
- 我能以某种方式阻止它扩展吗?我的意思是以用户友好的方式阻止我的包扩展?
(我真的必须阅读TeXBook当我有更多时间时会再来。
答案1
这是 TeX 自己做的,很难阻止它。摘自 TeXbook,第 240 页的 double danger bend:
一旦
\cr
检测到前导码末尾的 ,TeX 必须向前查看下一个标记是否为\noalign
或\omit
,然后展开宏,直到找到下一个非空格标记。如果标记不是\noalign
或\omit
,则将其放回并再次读取,然后 TeX 开始读取模板(仍在展开宏)。
问题似乎是插入的内容>{...}
被放入前言的\halign
,并且扩展发生在 TeX 从序言中读取模板之前。
答案2
TeX 的表格在扩展方面非常奇怪。下面是一个普通的 TeX 表格,其中有一列右对齐,一列左对齐
\halign{\hfil #&#\hfil \cr
a & bc \cr
de & f \cr}
第一行是“序言”,它告诉 TeX 如何处理每个单元格中的材料,用 表示#
。因此第一行变成\hfil a&bc\hfil \cr
。\hfil
代表可以拉伸以填充任意距离的空白空间,从而对齐单元格内容。
假设这种方法是既定的,让我们试着发明如何将规则添加到语言中,并戴上 Knuth 的帽子。[诚然,我可能会在某些导致 Knuth 和后来的引擎开发人员做出各种决定的原因上犯错,但我认为我的描述是一致的,尽管充满了时代错误。]
线应该加在两行之间,换句话说,就在 之后\cr
。我们假设一个\hline
,加在 之后,\cr
以产生一条水平线:
% Fake TeX code
\halign{\hfil #&#\hfil \cr
a & bc\cr
\hline
de & f\cr
\hline}
到目前为止描述的方法中,TeX 将采用\hline de
作为单元格的内容,第二行将变为\hfil \hline de&f\hfil\cr
。在这种特殊情况下,我们仍然可以通过\hline
在当前行之前插入材料来管理。但尾随\hline
是有问题的:TeX 会太晚意识到这里没有单元格:它会\hfil
在第一个单元格的前言部分插入,然后看到\hline
,在当前行之前插入规则,看到}
,并且必须以某种方式反转\hfil
。
更明智的做法是,当 TeX 看到 时\cr
,它应该向前看,看看 后面的内容\hline
。应该\hline
是原始的吗?这只允许引擎本身硬编码的规则类型或行间材料。不。Knuth 选择了一种更好的解决方案,即允许任意材料,使用\noalign
。那么就没有必要再提供\hline
,它只是一个未对齐的水平规则(\hrule
)。因此,真正简单的 TeX 方式是拥有一行(好吧,两条)是
\halign{#&#\cr
a & b \cr
\noalign{\hrule}
c & d \cr
\noalign{\hrule}}
大多数人会抱怨规则与文本太接近,但我们现在感兴趣的是扩展问题,而不是排版问题。自然,人们可能希望为 提供简写,\noalign{\hrule}
例如\hline
:
\def\hline{\noalign{\hrule}}
\halign{\hfil #&#\hfil\cr
a & b \cr \hline
c & d \cr \hline}
再次,我们最终会遇到一个问题:TeX 如何知道这个\hline
宏隐藏了\noalign
,以及 TeX 如何知道它不应该立即插入\hfil
。答案是,要找到\noalign
,TeX之后展开宏\cr
,然后插入序言中的材料。在每个寻找的单元格中都会发生相同的情况\omit
,但我不会深入探讨这一点。
这会导致问题:例如,在数学模式下的行为与在文本模式下的行为不同,宏不应该以 开头,\ifmmode
而应该以 开头\relax\ifmmode
。
\def\foo{\relax\ifmmode x^2\else the square of $x$\fi}
\halign{\hfil $#$\hfil &\hfil $#$\hfil \cr
\foo & y^2 \cr
z-2 & t\cr}
TeX 扩展了 之后的第一个标记\cr
,即\foo
,然后看到\relax
。这会停止扩展,它既不是\noalign
也不是\omit
,因此会插入前导码,进入数学模式。\ifmmode
然后执行测试。如果没有\relax
,\ifmmode
则会在插入前导码之前进行评估,并且结果为假。
为了防止这种扩展,eTeX 程序员决定(根据 eTeX 手册,在版本 2 中)\protected
宏会在这种情况下停止扩展。这与 eTeX 在其他从左开始完全扩展的设置中的扩展方式有些不一致,例如\romannumeral-`q
。在 Martin 的例子中(参见他对当前问题的回答),这最终非常有用,因为他可以使用受保护的宏来停止扩展。在其他情况下(请参阅 Peter Grill 的一些问题,例如关于如何提供包装器\cmidrule
),我们希望 TeX 能够更加努力地寻找隐藏的\noalign
。
答案3
感谢亨德里克的回答和与他和 Joseph Wright 聊天以及 Bruno Le Floch 的评论,我现在解决了与 扩展相关的所有问题tabulars
,即底层 plainTeX 原语\halign
。我想在这里列出它们,以防对其他人有用。
总结一下:这里的问题是\halign
扩展了后面的标记\cr
(这或多或少是 的纯 TeX 版本\\
)并&
查看 是否\noalign
跟在后面。当发现不可扩展的标记时,此扩展将停止。该包希望收集未扩展的这些标记。如果单元格仅为空或仅包含扩展为空或空格的宏(如或 )collcell
,则还存在扩展的问题。这是一个问题,因为代码将 视为结束标记,并且不喜欢包含的\\
\empty
\space
\\
卑鄙的伎俩很多。
这解决方案我现在想到的是:
- 宏在包的帮助下
\\
被处理。这样它就不会被扩展,但仍然正常工作。\robustify
etoolbox
\halign
- 将提供一个特殊的开始标记,该标记也是
\protected
不可扩展的。collcell
代码将在收集单元格内容时明确忽略(即吞噬)它。这允许用户将其放置在他们想要收集的单元格中,而无需任何扩展,并且不\relax
适合。这是必要的,因为像\texttiming
(tikz-timing
包)这样的特殊宏不能用不可扩展的宏来输入。
这不是最佳选择,因为它仍然需要用户采取一些行动,但我看不到任何其他解决方案。&
在我看来,重新定义并不是一个真正的选择。 \\
在标记收集停止后collcell
,其中包含的被临时重新定义,以便在原始标记扩展(使用)\cr
后重新启动收集,这会使 TeX 插入由 定义的标记。这样这些标记现在也被收集,这对于支持级联/很重要。\cr
\expandafter
<{ }
>{}
<{}
我很高兴听到有关此事的任何反馈。