在我正在编写的文档中,我有一个嵌套的表格结构,我想将其捕获为宏以供重复使用。我想使用新环境定义嵌套,但似乎不起作用。我尝试了三种方法:使用\newenvironment
、使用\NewEnviron
和使用普通宏。宏有效,但环境无效。
这是一个小例子,展示了我正在做的事情和遇到的问题的简化版本:
\documentclass{article}
\usepackage{environ}
\newenvironment{testTabA}{%
\multicolumn{3}{l}\begingroup
\begin{tabular}{ll}
}{%
\end{tabular}
\endgroup \\
}
\NewEnviron{testTabB}{%
\multicolumn{3}{l}{%
\begin{tabular}{ll}
\BODY
\end{tabular}
} \\
}
\newcommand\testTabC[1]{%
\multicolumn{3}{l}{%
\begin{tabular}{ll}
#1
\end{tabular}
} \\
}
\begin{document}
% No macros: This works
\begin{tabular}{lll}
1 & 2 & 3 \\
\multicolumn{3}{l}{
\begin{tabular}{ll}
a & b \\
\end{tabular}
} \\
\end{tabular}
% newenvironment: This fails with "Misplaced \omit"
\begin{tabular}{lll}
1 & 2 & 3 \\
\begin{testTabA}
a & b \\
\end{testTabA}
\end{tabular}
% NewEnviron: This fails with "Argument of \testTabB has an extra }"
\begin{tabular}{lll}
1 & 2 & 3 \\
\begin{testTabB}
a & b \\
\end{testTabB}
\end{tabular}
% Macro: This works
\begin{tabular}{lll}
1 & 2 & 3 \\
\testTabC{
a & b \\
}
\end{tabular}
\end{document}
作为 TeX 的新用户,我不知道发生了什么。有人能解释一下为什么环境示例会这样吗?
答案1
当 TeX 在基于原始语法树构建的对齐中启动一个单元时\halign
(这是 的情况tabular
),它想要检查一个名为 的特殊标记是否\omit
首先出现。
等一下!什么
\omit
?手册里没有这个!
不,它不在手册中:它是另一个 TeX 原语,LaTeX 用户不应该使用它,甚至知道关于它。简单地说,\multicolumn
是这样定义的,经过一些宏扩展后,它以 开头\omit
。实际上,它的扩展以 开头\multispan
,并且这个宏最终生成\omit
:
% 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}
% latex.ltx, line 5197:
\def\multispan{\omit\@multispan}
为了查看是否\omit
会出现,它会在对齐单元的开头扩展宏。一旦发现不可扩展的标记,该过程就会停止。最常见的情况是找到一个字符,当然它是不可扩展的;但也会\relax
停止此搜索。
如果\omit
发现后Misplaced \omit
这个不可扩展的标记,会引发错误。一个最小的例子是
\begin{tabular}{ll}
\relax\multicolumn{2}{c}{a}
\end{tabular}
在您的例子中,您位于\begin{testTabA}
单元格的开头。现在\begin
定义为
% latex.ltx, line 3944:
\def\begin#1{%
\@ifundefined{#1}%
{\def\reserved@a{\@latex@error{Environment #1 undefined}\@eha}}%
{\def\reserved@a{\def\@currenvir{#1}%
\edef\@currenvline{\on@line}%
\csname #1\endcsname}}%
\@ignorefalse
\begingroup\@endpefalse\reserved@a}
它\@ifundefined{...}{...}{...}
本身不会有问题,因为它是可扩展的:
% latex.ltx, line 788:
\def\@ifundefined#1{%
\expandafter\ifx\csname#1\endcsname\relax
\expandafter\@firstoftwo
\else
\expandafter\@secondoftwo
\fi}
但现在问题出现了:由于环境testTabA
是定义后,就会跟踪“false”分支,因此 TeX 认为
\def\reserved@a{...}
这就结束了合法的“时间框架” 。如果用 代替,\omit
也会发生同样的情况,因为问题出在 上。\NewEnviron
\newenvironment
\begin
所以\multicolumn
可以绝不隐藏在环境中,以实现您想要的目的。使用宏方法没有问题,但宏不应该有可选参数(除非它是用\DeclareExpandableDocumentCommand
from定义的xparse
,请参阅带有和不带有 xparse 的多列自定义宏)。