添加

添加

我正在尝试结合以下解决方案

解析以下可选参数结尾环境。这些选项只有三种可能的情况:

  1. 下标:_{subscript}
  2. 上标:^{superscript}
  3. 两个都下标和上标:_{subscript}^{superscript}

的情况^{subscript}_{superscript}不是必需的。

我原本以为将可选参数添加到\@MatrixWithParen辅助宏就足够了,但似乎选项并没有到达该宏。

尝试的解决方案:

  1. 使用\collect@body来调用\@MatrixWithParen以查找可选参数。根据 egreg 的评论,这不是可行的方法。MWE 产生左侧的结果,而所需的结果在右侧:

    在此处输入图片描述 在此处输入图片描述

  2. 将 中的内容存储\collect@body在宏中,并使用\AfterEndEnvironment调用宏来解析可选参数。这似乎也表明 指定要执行的宏无法使用可选参数\AfterEndEnvironment。同样\aftergroup, 和\AtEndEnvironment也不起作用:

    在此处输入图片描述

    如果存在多个bmatrix 环境,则此非解决方案还存在其他问题,但我认为可以通过额外的编程来解决。尝试将这个问题集中在解析 后面的可选参数上end{environment}

  3. 我的另一个想法是进行修改\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@endpeswitch 是小技巧的一部分,如果段落紧跟在声明后面\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,这意味着命令的执行顺序如下:

  1. 首先,评估条件;如果为假,则立即吞噬\if@endpe匹配的标记\fi(在本例中为\fi跟在后面的) ;如果为真,则只有标记从输入流中消失,并且继续执行,而该条件保持“待定”。\@doendpe\if@endpe

  2. \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定义,因为这些定义的执行顺序恰好是(但不是碰巧!)那个“正确”的。

相关内容