我正在尝试结合以下解决方案
解析以下可选参数结尾环境。这些选项只有三种可能的情况:
- 下标:
_{subscript}
- 上标:
^{superscript}
- 两个都下标和上标:
_{subscript}^{superscript}
的情况^{subscript}_{superscript}
是不是必需的。
我原本以为将可选参数添加到\@MatrixWithParen
辅助宏就足够了,但似乎选项并没有到达该宏。
尝试的解决方案:
使用
\collect@body
来调用\@MatrixWithParen
以查找可选参数。根据 egreg 的评论,这不是可行的方法。MWE 产生左侧的结果,而所需的结果在右侧:将 中的内容存储
\collect@body
在宏中,并使用\AfterEndEnvironment
调用宏来解析可选参数。这似乎也表明 指定要执行的宏无法使用可选参数\AfterEndEnvironment
。同样\aftergroup
, 和\AtEndEnvironment
也不起作用:如果存在多个
bmatrix
环境,则此非解决方案还存在其他问题,但我认为可以通过额外的编程来解决。尝试将这个问题集中在解析 后面的可选参数上end{environment}
。我的另一个想法是进行修改
\collect@body
以便调用允许两个可选参数的宏,但这超出了我的理解。
笔记:
- 这个问题不是
()
如何用而不是 来获取矩阵[]
(这pmatrix
很容易做到),因为这只是一个 MWE,旨在用最少的附加代码来说明我的实际用例。也就是说,根据我的另一个问题,我需要#1
是参数,\@MatrixWithParen
而不仅仅是在宏之外添加的下标(即,\@MatrixWithParen
必须是调用实际下标和上标的那个)。
相关问题:
一般问题:
- 如何解析特定的可选参数
\end{environment}
?
1. 调用宏解析可选参数\collect@body
。
\documentclass{article}
\usepackage{mathtools}
\usepackage{xparse}
\usepackage{xcolor}
\makeatletter
\NewDocumentCommand{\@MatrixWithParen}{m k_ k^}{%
\left(
\begin{matrix}%
#1% content
\end{matrix}
\right)
%% These seem to have no effect
\IfValueT{#2}{_{\textcolor{red}{#2}}}%
\IfValueT{#3}{^{\textcolor{blue}{#3}}}%
}%
\RenewDocumentEnvironment{bmatrix}{}{%
{\ifnum0=`}\fi\collect@body\@MatrixWithParen%
}{%
\ifnum0=`{\fi}%
}%
\makeatother
\begin{document}
\begin{align*}
A &= \begin{bmatrix} %% Both subscript and superscript
a_{11} & a_{12} \\
a_{21} & a_{22} \\
\end{bmatrix}_{2 \times 2}^{-1}
\\
B &= \begin{bmatrix} %% Only superscript
1 & -1 \\
-1 & 1 \\
\end{bmatrix}^{2}
\\
C &= \begin{bmatrix} %% Only subscript
a & b \\
b & d \\
\end{bmatrix}_{2}
\end{align*}
\end{document}
2:尝试使用\AfterEndEnvironment
:
\documentclass{article}
\usepackage{amsmath}
\usepackage{xparse}
\usepackage{xcolor}
\usepackage{etoolbox}
\makeatletter
\newcommand*{\@MatrixContents}{}%
\newcommand*{\@PostBMatrix}{\@PostBMatrixAux}%
\NewDocumentCommand{\@PostBMatrixAux}{k_k^}{%
\@MatrixContents
\IfValueT{#1}{_{\textcolor{red}{#1}}}%
\IfValueT{#2}{^{\textcolor{blue}{#2}}}%
}
\NewDocumentCommand{\@MatrixWithParen}{m}{%
\gdef\@MatrixContents{%
\left(
\begin{matrix}
#1% content
\end{matrix}
\right)
}%
%\aftergroup{\@PostBMatrix}%
%\AtEndEnvironment{bmatrix}{\@PostBMatrix}% Not sure why this has slightly
\AfterEndEnvironment{bmatrix}{\@PostBMatrix}% different spacing than this.
}%
\RenewDocumentEnvironment{bmatrix}{}{%
{\ifnum0=`}\fi\collect@body\@MatrixWithParen%
}{%
\ifnum0=`{\fi}%
}%
\makeatother
\begin{document}
\[
A = \begin{bmatrix}
a_{11} & a_{12} \\
a_{21} & a_{22} \\
\end{bmatrix}_{2 \times 2}^{-1}
\]
\end{document}
答案1
的问题在于,后面\AfterEndEnvironment
还有一段额外\if@ignore\@ignorefalse\ignorespaces\fi
的代码。因此,参数k_k^
永远无法到达其预期的下标和上标。您可以使用额外的参数来解决这个问题u{\fi}
(或者,如果您想更加谨慎,可以使用u{\if@ignore\@ignorefalse\ignorespaces\fi}
,因此如果不存在该代码,则会给出错误,u{\fi}
为简单起见,我将使用它)。
完整代码
\documentclass{scrartcl}
\usepackage{etoolbox}
\usepackage{mathtools}
\usepackage{xparse}
\usepackage{xcolor}
\makeatletter
\RenewDocumentEnvironment{bmatrix}{}
{{\ifnum0=`}\fi\collect@body\savematrix}
{\ifnum0=`{\fi}}
\newcommand\matrixbody{}
\NewDocumentCommand\savematrix{m}{\gdef\matrixbody{#1}}
\AfterEndEnvironment{bmatrix}{\printmatrix}
\NewDocumentCommand\printmatrix{u{\fi} k_ k^}
{\left(\begin{matrix}%
\matrixbody
\end{matrix}\right)%
\IfValueT{#2}{_{\textcolor{red}{\!#2}}}%
\IfValueT{#3}{^{\textcolor{blue}{\!#3}}}}
\makeatother
\begin{document}
\begin{align*}
A &= \begin{bmatrix} %% Both subscript and superscript
a_{11} & a_{12} \\
a_{21} & a_{22} \\
\end{bmatrix}_{2 \times 2}^{-1}
\\
B &= \begin{bmatrix} %% Only superscript
1 & -1 \\
-1 & 1 \\
\end{bmatrix}^{2}
\\
C &= \begin{bmatrix} %% Only subscript
a & b \\
b & d \\
\end{bmatrix}_{2}
\end{align*}
\end{document}
作为一般建议,如果您不想查看定义或源代码(如果您不知道从哪里查看,这可能会有点乏味),您可以定义一个辅助宏,它会将“那里有什么”打印到文档中,让您轻松检查可能出现的问题。例如,如果您定义
\def\qq#1\qq{#1\par\texttt{\detokenize{#1}}}
宏将抓取直到的所有内容\qq
,将其放回原处(以便代码编译),并将在\tt
字体中看到的所有代码写入新段落中(请记住将分隔符\qq
(或您选择的任何名称)放在数学模式之外)。以下是几个示例。
想象一下你使用过\AtEndEnvironment
它但没有起作用,检查它为什么不起作用的一个快速方法是
\AtEndEnvironment{bmatrix}{\qq}
$\begin{bmatrix} a \end{bmatrix}$ \qq
它会立即显示“那里有什么”
它快速而直接,你现在就知道那里到底发生了什么。现在如果你使用\AfterEndEnvironment
\AfterEndEnvironment{bmatrix}{\qq}
$\begin{bmatrix} a \end{bmatrix}$ \qq
这就是我所做的,因此我们需要抓住这四个令牌,我决定去u{\fi}
抓住直到 \fi
。
虽然它不一定能起作用,但很多时候它能让你快速了解正在发生的事情。如果出现问题,你可以尝试一下,它只需一秒钟,可能会解决你的问题。
答案2
比@Manuel 的解决方案更简单、更安全(无论如何都非常聪明):
\documentclass{scrartcl}
% \usepackage{etoolbox} isn't actually necessary
\usepackage{mathtools}
\usepackage{xparse}
\usepackage{xcolor}
\makeatletter
\renewenvironment*{bmatrix}{%
{\ifnum0=`}\fi
\collect@body\processmatrix
}{%
\ifnum0=`{\fi}%
}
\NewDocumentCommand \processmatrix { m } {%
\begingroup
\RenewDocumentCommand \end {mk_k^}{%
\endgroup
\end{##1}%
\left(\begin{matrix}#1\end{matrix}\right)%
\IfValueT{##2}{_{\textcolor{red} {\!##2}}}%
\IfValueT{##3}{^{\textcolor{blue}{\!##3}}}%
}%
}
\makeatother
\begin{document}
\begin{align*}
A &= \begin{bmatrix} %% Both subscript and superscript
a_{11} & a_{12} \\
a_{21} & a_{22} \\
\end{bmatrix}_{2 \times 2}^{-1}
\\
B &= \begin{bmatrix} %% Only superscript
1 & -1 \\
-1 & 1 \\
\end{bmatrix}^{2}
\\
C &= \begin{bmatrix} %% Only subscript
a & b \\
b & d \\
\end{bmatrix}_{2}
\\
D &= \begin{bmatrix} %% Neither subscript nor superscript
a & b \\
b & d \\
\end{bmatrix}
\\
E &= \begin{bmatrix} %% Example of nesting
\begin{bmatrix} x & y \\ -y & x \end{bmatrix}^{h}
& \begin{bmatrix} a & b \\ -b & a \end{bmatrix}^{k} \\
\begin{bmatrix} a & -b \\ b & a \end{bmatrix}_{h}
& \begin{bmatrix} x & -y \\ y & x \end{bmatrix}_{k}\\
\end{bmatrix}_{2 \times 2}^{-1}
\end{align*}
\end{document}
看起来它也能正确处理嵌套。xparse
无论如何,这是滥用。
添加
其中一条评论要求我“详细说明为什么这样做‘更安全’”。好吧,有一个普遍的原因,那就是这个解决方案对宏中包含的代码没有做出任何特殊的假设\end
,因此即使后者发生变化,它也会继续工作;这当然符合“更安全”的条件,即“更强大”。
然而,当我最初写下这句话时,我脑海中有一个更具体的问题,后来我意识到,这个问题是基于一个错误的前提;这个问题在对 Manuel 的回答的几条评论中被简要讨论过,当我意识到我的错误时(实际上是在 48 小时的宽限期之后),我删除了这些评论。我会看看是否有办法取消删除它们,从而让它们可见以供参考。
天知道为什么,我曾假设该\AfterEndEnvironment
命令通过 注入了(一个扩展为 的宏)其第二个参数中指示的代码,该代码位于被修补环境所暗示的组之后,但事实\aftergroup
并非如此。(很少有事情比你半无意识地做出的假设更危险。)我的想法是,如果真是这样,那么如果将开关\if@endpe
设置为 true,Manuel 设计的机制就会被阻止,例如,所有基于 或 的环境都是这样做的\list
。\trivlist
“我在评论中或多或少说过,该解决方案对于问题中提出的情况很有效,但总体上可能不够充分,例如当应用于“列表制作”环境时”。
让我们简单解释一下这个问题。\if@endpe
switch 是小技巧的一部分,如果段落紧跟在声明后面\end{environment}
,中间没有空行,则段落缩进将被抑制。例如,如果你说
\begin{center}
Text inside.
\end{center}
Text after.
那么“之后的文本”是不是缩进,因为它前面有\noindent
。实现宏的代码\end
,即
\def\end#1{%
\csname end#1\endcsname\@checkend{#1}%
\expandafter\endgroup\if@endpe\@doendpe\fi
\if@ignore\@ignorefalse\ignorespaces\fi}
查看此开关,如果为真,则执行\@doendpe
宏,该宏负责设置和重置\par
和的定义\everypar
,以实现所需的效果。(我不会在这里提供更多细节:如果您感兴趣,请查看\@endparenv
中的宏定义ltlists.dtx
。)但请注意,它说\expandafter\endgroup\if@endpe
,这意味着命令的执行顺序如下:
首先,评估条件;如果为假,则立即吞噬
\if@endpe
匹配的标记\fi
(在本例中为\fi
跟在后面的) ;如果为真,则只有标记从输入流中消失,并且继续执行,而该条件保持“待定”。\@doendpe
\if@endpe
\endgroup
被执行,并且在当前组之后保存以插入的令牌被准确地传送到他的点。
\AfterEndEnvironment
因此我们看到,如果用于\aftergroup
完成其任务的命令确实如此,\printmatrix
那么 Manuel 使用的宏将会发现在两个参数之前插入了以下标记k
:
如果
\if@endpe
为假,\if@ignore\@ignorefalse\ignorespaces\fi
没有待决条件;
如果
\if@endpe
是真的,\@doendpe\fi\if@ignore\@ignorefalse\ignorespaces
条件
\if@endpe
仍未满足。
您可以看到第二种情况可能有问题……
所有这些都可以通过一些简单的实验来证实,当然不是使用\AfterEndEnvironment
,而是\aftergroup
插入一个类似于\qq
测试宏的宏,Manuel 在他的答案的第二部分建议,“在”(在上面指定的意义上)某个环境“之后”。
% My standard header for TeX.SX answers:
\documentclass[a4paper]{article} % To avoid confusion, let us explicitly
% declare the paper format.
\usepackage[T1]{fontenc} % Not always necessary, but recommended.
% End of standard header. What follows pertains to the problem at hand.
\def\qq#1\qq{ (%
level:~\number\currentiflevel,
type:~\number\currentiftype,
branch:~\number\currentifbranch
\space \detokenize{#1})#1%
}
\makeatletter
\newenvironment*{grabtest}{}{%
\aftergroup\qq
% \@endpetrue
}
\makeatother
\begin{document}
Text before.
\begin{grabtest}Text inside\end{grabtest}\qq
Text after.
\end{document}
注释或取消注释该\@endpetrue
命令将在输出中显示上面描述的两种结果。
以下源文件显示了如果将 Manuel 的方法与结合使用会发生什么情况\aftergroup
:这正是我在认为我的解决方案比他的“更安全”时所想到的。
% My standard header for TeX.SX answers:
\documentclass[a4paper]{article} % To avoid confusion, let us explicitly
% declare the paper format.
\usepackage[T1]{fontenc} % Not always necessary, but recommended.
% End of standard header. What follows pertains to the problem at hand.
\usepackage{xparse}
\makeatletter
\NewDocumentCommand \qq {u{\if@ignore\@ignorefalse\ignorespaces\fi} k_ k^ } {%
\unskip\space(%
\IfValueTF{#2}{subscript: $#2$}{no subscript},
\IfValueTF{#3}{superscript: $#3$}{no superscript})%
}
\newenvironment*{genericthing}{}{%
\aftergroup\qq
\@endpetrue
}
\makeatother
\begin{document}
Text before.
\begin{genericthing}Text inside\end{genericthing}_{a\times b}^{\pi}
Text after.
\end{document}
该示例可以编译,但是您会收到有关条件不完整的警告:
(\end occurred when \iftrue on line 31 was incomplete)
在这种情况下,这不会造成伤害,但一般来说可以有问题!
最后说明一下:Manuel 的解决方案在嵌套的情况下可以正常工作,尽管使用了\global
定义,因为这些定义的执行顺序恰好是(但不是碰巧!)那个“正确”的。