使用对齐运算符 (&) 将参数传递给另一个对齐环境中的宏

使用对齐运算符 (&) 将参数传递给另一个对齐环境中的宏

我正在尝试编写宏以在数学模式下快速格式化矩阵。这是一个简单的工作示例:

\documentclass{article}
\usepackage{amsmath}
\newcommand{\pmat}[1]{\begin{pmatrix}#1\end{pmatrix}}

\begin{document}
\begin{displaymath}
M = \pmat{1&0\\0&1}
\end{displaymath}
\end{document}

这也适用于之内一个对齐的环境(tablealign环境、cases环境等):

\documentclass{article}
\usepackage{amsmath}
\newcommand{\pmat}[1]{\begin{pmatrix}#1\end{pmatrix}}

\begin{document}
\begin{align}
M &= \pmat{1&0\\0&1}
\end{align}
\end{document}

通常,在align环境中&,符号告诉 LaTeX 开始一个新列,但由于矩阵值包含在{}组标记之间,因此 LaTeX 不会尝试解析1&0\\0&1参数,因此一切都按预期进行。

让我们创建一个不使用组标记{}来表示宏参数的新宏。例如,使用方括号对参数进行分组是相当常见的,如以下工作示例所示(试试看!):

\documentclass{article}
\usepackage{amsmath}
\newcommand{\bmat}[1][]{\begin{bmatrix}#1\end{bmatrix}}

\begin{document}
\begin{displaymath}
M = \bmat[1&0\\0&1]
\end{displaymath}
\end{document}

到目前为止一切顺利。问题来了!如果我尝试使用我的新宏\bmat 之内另一个对齐的环境:

% DOES NOT WORK!!!
\documentclass{article}
\usepackage{amsmath}
\newcommand{\bmat}[1][]{\begin{bmatrix}#1\end{bmatrix}}

\begin{document}
\begin{align}
M &= \bmat[1&0\\0&1]
\end{align}
\end{document}

我收到一个错误,因为 LaTeX 尝试将&矩阵参数内的参数解释为外部align环境的一部分。

\pmat{}LaTeX 处理第一个宏和第二个宏的方式之间的区别\bmat[]在于,组标记{}会阻止 LaTeX 解析花括号中包含的参数,直到\pmat调用宏之后。另一方面,当 LaTeX 看到\bmat[arg]宏时,参数没有被组标记包围,因此 LaTeX 会尝试解析它被 吃掉\bmat。额外的&字符会破坏环境的语法align,因此 LaTeX 会崩溃。至少,这是我对发生的事情的理解,但我可能错了!

因此,问题是是否有可能强制 LaTeX 以与案例中[]相同的方式处理案例中的参数{}(而不会引起其他问题)。换句话说,我希望能够编写一个\bmat[1&0\\0&1]在对齐环境中工作但保留方括号语法的宏 a-la。

结论:

[]感谢 egreg(需要xparse并且必须包含在单独的包文件中或序言中的\makeatletter和之间\makeatother),这是具有工作语法的最终宏:

\DeclareDocumentCommand\mat{}{{\ifnum\z@=`}\fi\@mat}
\DeclareDocumentCommand\@mat{ g o d() d|| }
{
    \IfNoValueTF{#1}
    {
        \IfNoValueTF{#2}
        {
            \IfNoValueTF{#3}
            {
                \IfNoValueTF{#4}
                {()}
                {\begin{vmatrix}#4\end{vmatrix}}
            }
            {
                \begin{pmatrix}#3\end{pmatrix}
                \IfNoValueTF{#4}{}{|#4|}
            }
        }
        {
            \begin{bmatrix}#2\end{bmatrix}
            \IfNoValueTF{#3}{}{(#3)}
            \IfNoValueTF{#4}{}{|#4|}
        }
    }
    {
        \begin{matrix}#1\end{matrix}
        \IfNoValueTF{#2}{}{[#2]}
        \IfNoValueTF{#3}{}{(#3)}
        \IfNoValueTF{#4}{}{|#4|}
    }
    \ifnum\z@=`{\fi}
}

{}此宏的工作方式类似于具有不同参数分隔符、[]()或 的重载函数||。可能有很多原因可以解释为什么这是一个糟糕的想法 --- 所以请在下面发表您的评论。我希望听听那些比我更了解的人的意见。

答案1

你可以做到,但是我强烈建议您不要这样做。使用[]作为分隔符对强制的争论。

\documentclass{article}
\usepackage{amsmath}

\makeatletter
\newcommand\bmat{{\ifnum\z@=`}\fi\@bmat}
\def\@bmat[#1]{\begin{bmatrix}#1\end{bmatrix}\ifnum\z@=`{\fi}}
\makeatother

\begin{document}
$\bmat[1&0\\0&1]$
\begin{align}
M &= \bmat[1&0\\0&1]
\end{align}
\end{document}

上述示例的结果

(注意:我减小了文本宽度以缩短输出。)

\eegroup当你理解了这个技巧,你就可以使用它了。:)提示:在 TeXbook 的附录 D 中查找。


我认为,定义\mat查找下一个标记来决定分隔符是错误的。我会这样做:

\newcommand{\mat}[2][]{\begin{#1matrix}#2\end{#1matrix}}

以便

\mat{1&2\\3&4}    % no fences
\mat[b]{1&2\\3&4} % brackets
\mat[p]{1&2\\3&4} % parentheses
\mat[v]{1&2\\3&4} % vertical lines
\mat[V]{1&2\\3&4} % double vertical lines
\mat[B]{1&2\\3&4} % braces

会做得更好,麻烦更少,可读性更好。

就我个人而言,我会坚持使用长格式,但这只是个人喜好问题。

答案2

为什么会发生这种情况相当有趣,因为原始代码

\begin{align}
 M = \begin{bmatrix} 1 & 0 \\ 0 & 1 \end{bmatrix}
\end{align}

即使数组分隔符不在括号之间,它也能正常工作。原因是它bmatrix本身专门插入括号来实现这一点,当扫描到达分隔符时,TeX 已经读取了左括号并知道它应该保护它们。

在您的代码中,似乎\bmat在读取之前,将替换为其扩展,即上面的代码&,但实际上,&此时已经读取了:它需要由宏参数抓取器拾取。也就是说,即使没有&“执行”,它也是看到,显然,即使在进行标记化时,TeX 也会检测到数组单元的结尾。

因此,无法使用常规\newcommand\bmat[1][]{}语法来定义“只在数组中有效”的命令;egreg 的回答展示了如何自己编写一个接受相同语法但偷偷插入括号的\catcode命令。在扫描参数之前,可能还可以进行更改,使其变成[]功能性括号字符,但尽管如此,我建议您重新设计宏,不要使用可选参数。作为一种折衷方案,您可以{\bmat[1&0\\0&1]}像 Scott H. 建议的那样,在它周围放置括号:。

答案3

在 TeX 中,扫描内部数据\halign非常特殊。请注意,这是LaTeX 环境的\halign 原始实现。bmatrixalign

\halign在 内时\halign,所有操作均可行,但宏扩展必须小心谨慎。@Ryan 的回答中的示例

\begin{align}
   M &= \begin{bmatrix} 1 & 0 \\ 0 & 1 \end{bmatrix}
\end{align}

在 TeX 原始级别上(粗略地说)执行以下操作:

\halign\bgroup #&#\cr 
   M &= \vbox\bgroup\halign\bgroup &#\cr 1&0\cr 0&1\cr\egroup\egroup 
\cr\egroup

实现\begin{bmatrix}为一个宏,它扩展为 \vbox\bgroup\halign\bgroup &#\cr(不完全是,但这个宏的更多操作对于我们的情况并不重要)并\end{bmatrix}扩展为 \cr\egroup\egroup。重要的是,宏\begin{bmatrix} 不会读取由 分隔的参数\end{matrix},因此这个 LaTeX 环境的两个部分“独立”工作。数据 在主 TeX 处理器级别作为内部 数据1&0\cr 0&1读取。\halign\halign

当带有参数的宏被读取到外部数据时,情况就完全不同了\halign。扫描宏参数的主要规则是:

仅当处理在...对内时&, (或\cr)才被解释为&(或)\cr{},即在 catcode 1 和 2 的字符对内。并且,无论这对字符是否在宏之外( 例如在排版输出中\bgroup... \egroup),或者这对字符是否用于宏参数周围作为读取宏参数的语法规则,都是无关紧要的。这意味着这两个示例都有效:

\def\hh[#1]{\vbox\bgroup\halign\bgroup &##\cr #1\cr\egroup\egroup}

\halign\bgroup #&#\cr
   M & = {\hh[1&0\cr 0&1]}
\cr\egroup

或者

\def\hh#1{\vbox\bgroup\halign\bgroup &##\cr #1\cr\egroup\egroup}

\halign\bgroup #&#\cr
   M & = \hh{1&0\cr 0&1}
\cr\egroup

另一方面,当不满足上述规则时,或&永远 \cr不会在参数中“按原样”扫描:它立即被替换为单元格声明的第二部分,\endtemplate在扫描参数时禁止使用。这意味着此示例不起作用:

\def\hh[#1]{\vbox\bgroup\halign\bgroup &##\cr #1\cr\egroup\egroup}

\halign\bgroup #&#\cr
   M & = \hh[1&0\cr 0&1]
\cr\egroup

此外,这还不是全部。所有宏都会在每个数据单元的开头展开,直到找到不可扩展且无空格的标记。此时展开宏时,&将“按原样”进行扫描。因此,它可以工作:

\def\h[#1]{\vbox\bgroup\halign\bgroup &##\cr #1\cr\egroup\egroup}

\halign\bgroup #&#\cr
   M & \expandafter=\h[1&0\cr 0&1]
\cr\egroup

相关内容