表格单元格中第一个标记的扩展

表格单元格中第一个标记的扩展

背景

我目前正在做一些工作collcelltabular包收集/单元的内容array并将其提供给用户定义的宏,例如使用>{\collectcell\mymacro}c<{\endcollectcell}

问题

我发现每个单元格的第一个标记在插入代码之前都会被扩展>{...}。我不确定这是如何以及在哪里完成的。有人知道吗:

  1. 什么扩展了这个标记(即array/ tabularLaTeX 代码或 plainTeX\halign代码或其他?)以及在哪里
  2. 为什么这样做(我有一些关于工资的想法,但我对“官方”原因非常感兴趣)
  3. 我能以某种方式阻止它扩展吗?我的意思是以用户友好的方式阻止我的包扩展?

(我真的必须阅读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\\卑鄙的伎俩很多。

解决方案我现在想到的是:

  • 宏在包的帮助下\\被处理。这样它就不会被扩展,但仍然正常工作。\robustifyetoolbox\halign
  • 将提供一个特殊的开始标记,该标记也是\protected不可扩展的。collcell代码将在收集单元格内容时明确忽略(即吞噬)它。这允许用户将其放置在他们想要收集的单元格中,而无需任何扩展,并且不\relax适合。这是必要的,因为像\texttimingtikz-timing包)这样的特殊宏不能用不可扩展的宏来输入。
    这不是最佳选择,因为它仍然需要用户采取一些行动,但我看不到任何其他解决方案。&在我看来,重新定义并不是一个真正的选择。
  • \\在标记收集停止后collcell,其中包含的被临时重新定义,以便在原始标记扩展(使用)\cr后重新启动收集,这会使 TeX 插入由 定义的标记。这样这些标记现在也被收集,这对于支持级联/很重要。\cr\expandafter<{ }>{}<{}

我很高兴听到有关此事的任何反馈。

相关内容