对齐星号命令内的对齐制表符

对齐星号命令内的对齐制表符

这是我想要做的一个最简单的例子。

\documentclass{article}

\usepackage{amsmath}

\makeatletter
\newcommand{\mylinebreak}{\@ifstar\@mylinebreak\@@mylinebreak}
\newcommand{\@mylinebreak}{&\\}
\newcommand{\@@mylinebreak}{&&\\}
%\let\mylinebreak\@@mylinebreak
\makeatother

\begin{document}
\begin{align*}
 a&=b\mylinebreak
  &=c.
\end{align*}
\end{document}

这给出了错误信息

! Misplaced alignment tab character &.
\@@mylinebreak ->&
                  &\\
l.16 \end{align*}

出了什么问题?我该如何让它正常工作?

奇怪的是,如果我删除%或者使用,我不会收到任何错误消息\mylinebreak*

我猜测可以使用涉及主/平衡计数器的技巧来解决该问题,但我无法使其发挥作用。

答案1

您正在定义\\为向前查找*(顺便说一下,标准命令已经向前查找了*),但在执行此操作时,它会“看到”&并且下一个单元格在上一行通过插入原始单元格结束之前开始\\

中的定义amsmath

\def\math@cr{\relax\iffalse{\fi\ifnum0=`}\fi
  \@ifstar{\global\@eqpen\@M\math@cr@}%
          {\global\@eqpen
             \ifnum\dspbrk@lvl <\z@ \interdisplaylinepenalty
              \else -\@getpen\dspbrk@lvl \fi
           \math@cr@}}
\def\math@cr@{\new@ifnextchar[\math@cr@@{\math@cr@@[\z@]}}
\def\math@cr@@[#1]{\ifnum0=`{\fi \iffalse}\fi\math@cr@@@
  \noalign{\vskip#1\relax}}

此特殊\relax\iffalse{\fi\ifnum0=`}\fi设计旨在通过阻止 halign 模板在此点结束单元格来避免此问题。

请注意,此时它已经用于\@ifstar定义\\*以防止分页。

目前尚不清楚使用的目的是什么,因为该构造的行为与空单元在几乎所有情况下都不执行任何操作&\\相同。\\

答案2

总结

简单的解决方案:对*-variant使用更强大的检查。

\documentclass{article}

\usepackage{amsmath}

\NewDocumentCommand{\mylinebreak}{s}{%
  \IfBooleanTF{#1}{&x\\}{&x&=y\\}%
}

\begin{document}
\begin{align*}
 a&=b\mylinebreak
  &=c.\\
 a&=b\mylinebreak*
  &=c.\\
\end{align*}
\end{document}

在此处输入图片描述

我添加了一些内容以便更好地查看选择是否正确。

如果您运行的是 2020-10-01 版本之前的 LaTeX 版本,那么您还需要\usepackage{xparse}

问题分析

让我们看看会发生什么:\mylinebreak变成

\@ifstar\@mylinebreak\@@mylinebreak

\@ifstar根据定义amsmath

\def\@ifstar#1#2{\new@ifnextchar *{\def\reserved@a*{#1}\reserved@a}{#2}}

以上变成

\new@ifnextchar*{\def\reserved@a{\@mylinebreak}\reserved@a}{\@@mylinebreak}

让我们看看\new@ifnextchar做了什么:

\long\def\new@ifnextchar#1#2#3{%
  \let\reserved@d= #1%
  \def\reserved@a{#2}\def\reserved@b{#3}%
  \futurelet\@let@token\new@ifnch
}

因此扩展继续为(换行符只是为了方便阅读)

\let\reserved@d= *
\def\reserved@a{\def\reserved@a*{\@mylinebreak}\reserved@a}
\def\reserved@b{\@@mylinebreak}
\futurelet\@let@token\new@ifnch &

尾部&来自这样一个事实:\mylinebreak在标记化过程中,后面的换行符会被忽略。不幸的是,我们处于对齐状态,因此一旦 TeX 扫描到&,它就会插入模板的一部分。而且,确实,具有高\errorcontextlines读取值的错误消息

! Misplaced alignment tab character &.
\@@mylinebreak ->&
                  &\\
<to be read again> 
                   }
<template> }
            $}\ifmeasuring@ \savefieldlength@ \fi \set@field \hfil \endtempl...
<argument>  a&=b\mylinebreak &
                              =c. 

\@let@token设置为}。这可以通过\show\@let@token在 的开头注入来确认\new@ifnch

\@let@token=end-group character }

好的,让我们更进一步:\new@ifnch收益率的扩大

\ifx@let@token\reserved@d\let\reserved@b\reserved@a\fi\reserved@b

在我们的例子中,\ifx测试返回 false,因此我们仍然

\保留@b}&

并且这会中断,因为\reserved@b变成\@@mylinebreak

相关内容