自定义环境与表格内的宏

自定义环境与表格内的宏

在我正在编写的文档中,我有一个嵌套的表格结构,我想将其捕获为宏以供重复使用。我想使用新环境定义嵌套,但似乎不起作用。我尝试了三种方法:使用\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可以绝不隐藏在环境中,以实现您想要的目的。使用宏方法没有问题,但宏不应该有可选参数(除非它是用\DeclareExpandableDocumentCommandfrom定义的xparse,请参阅带有和不带有 xparse 的多列自定义宏)。

相关内容