这是我想要做的一个最简单的例子。
\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
。