有没有办法检测宏前面的“\left”?

有没有办法检测宏前面的“\left”?

有没有办法让宏可选地以\left或为前缀\right,并根据该前缀进行不同的扩展?例如,扩展\bra{x}\langle x |,但\left\bra{x}扩展为\left\langle x \middle|?如果它也可以与显式大小前缀一起使用,则可获得额外奖励,将该大小同时赋予\langle|

以下是使用这些宏可以执行的操作的一些示例:

假设有相应的\ket宏,那么可以这样写

\left\bra{x} \frac{\hat p}{m} \right\ket{x}

并将其扩展到

\left\langle x \middle| \frac{\hat p}{m} \middle| x \right\rangle

也就是说,括号的大小取决于参数本身以及参数之间的内容。另一方面,

\bra{x} \frac{\hat p}{m} \ket{x}

只会扩展为

\langle x | \frac{\hat p}{m} | \rangle

也就是说,没有扩展。由于-前缀宏\left的扩展中恰好有一个\left,并且-前缀宏\right的扩展中恰好有一个\right,因此也可以将其与普通分隔符(包括“伪分隔符” .)组合使用。例如,

\left\langle{x^2}\right.

只会根据参数内部的内容进行扩展。

(注意:我知道 brackett 包,所以不需要指出这一点。请注意,它也不是\left/\right东西。)

答案1

恐怕这是一个没有真正解决方案的好问题;除非重新定义\left,但我不愿意这样做。Werner 的定义相当安全,可能会扩展到\bigl公司。但这并不比带有可选参数的语法更易于管理。

下面的方法可行,但是有一个很大的缺点:如果\bra被发现在由另一个 组成的组中\left,那么所有 都会惨死。

\let\TeXmiddle\middle

\def\bra#1{\langle#1
  \ifnum\currentgrouptype=16
    \neutralizeright
  \else
      \neutralizemiddle
  \fi}

\def\neutralizeright{%
  \def\middle##1{\TeXmiddle##1}%
  \let\rightket\right
  \let\right\relax
  \aftergroup\neutralizeright}
\def\neutralizemiddle{\let\middle\relax}
\let\rightket\relax
\def\ket#1{#1\rightket\rangle}

$\bra{x}\middle|y\middle|\ket{z}$

$\left\bra{x} \middle| \frac{a}{b} \middle| \right\ket{z}$

第一个公式中的\middle是可选的,因为它不执行任何操作。但是,正如我之前所说,

$\left( \bra{x}|y|\ket{z} \right)$

会死。条件\ifnum\currentgrouptype=16检查我们是否处于由 发起的组中\left。由于\left扩展了以下标记以找到分隔符,因此\bra在其后扩展了该标记,业务可以继续。但没有办法知道什么时候 \left已经打开,如第三个示例所示。

我更喜欢这样的语法

\bra{x} | y | \ket{z}
\bra[\left]{x} | y | \ket{z}
\bra[\big]{x} | y | \ket{z}

可以用一种相当直接的方式来定义。下面是一种方法:

\documentclass[a4paper]{article}
\usepackage{amsmath}

\begingroup\lccode`~=`|
  \lowercase{\endgroup\def~}{\braketmiddle}
\edef\pipedel{\delimiter\the\delcode`|}
\mathchardef\pipechar\mathcode`|

\newcommand{\bra}[2][]{\begingroup
  \mathcode`\|=\string"8000
  #1\langle
  \csname @bra\string#1ket@\endcsname#2}
\newcommand{\ket}[1]{%
  #1\braketright\rangle\endgroup}
\expandafter\def\csname @braket@\endcsname{%
  \def\braketmiddle{\pipechar}
  \def\braketright{}
}
\expandafter\def\csname @bra\string\left ket@\endcsname{%
  \def\braketmiddle{\middle\pipedel}
  \def\braketright{\right}
  \expandafter\aftergroup\csname @bra\string\left ket@\endcsname
}
\expandafter\def\csname @bra\string\bigl ket@\endcsname{%
  \def\braketmiddle{\big\pipedel}
  \def\braketright{\bigr}
}
\expandafter\def\csname @bra\string\Bigl ket@\endcsname{%
  \def\braketmiddle{\Big\pipedel}
  \def\braketright{\Bigr}
}
\expandafter\def\csname @bra\string\biggl ket@\endcsname{%
  \def\braketmiddle{\bigg\pipedel}
  \def\braketright{\biggr}
}
\expandafter\def\csname @bra\string\Biggl ket@\endcsname{%
  \def\braketmiddle{\Bigg\pipedel}
  \def\braketright{\Biggr}
}

\begin{document}

%\tracingmacros=1 \tracingonline=1
$\bra{x} | \bra{1}|2|\ket{3} y | \ket{z}$

$\bra[\left]{x} | \bra{1}|2|\ket{3}\dfrac{y}{2} | \ket{z}$

$\bra[\bigl]{x} | y | \ket{z}$

$\bra[\Bigl]{x} | y | \ket{z}$

$\bra[\biggl]{x} | y | \ket{z}$

$\bra[\Biggl]{x} | y | \ket{z}$

\end{document}

这甚至允许嵌套。

关于第二种解决方案的一些评论。语法是

\bra[<size>]{x} | y | \ket{z}

其中y |部分是可选的。为了正确处理|字符,我们将为其分配 ,\mathcode"8000这意味着它将表现得像一个活动字符。因此,首先我们将 的活动版本定义|\braketmiddle,而后者又将根据参数以不同的方式定义<size>。其默认定义(无<size>规范)是\pipechar由 定义的\mathchardef\pipechar=\mathcode`|,即与普通的 一样|。因此|括号中将给出一个简单的竖线。

当给出了尺寸规范时,\braketmiddle将变为\middle\pipedel\big\pipedel,其他的也类似;\middle\big并且公司希望在它们后面看到一个分隔符,所以我们定义了

\edef\pipedel{\delimiter\the\delcode`|}

每个角色都有一个\delcode(大多数角色的值为零,这意味着他们不是分隔符)。|字符有一个,我们用它访问它\the\delcode`|(我们不能说\middle|,因为我们已经为分配了特殊的数学代码|)。因此,在我们的上下文中,\middle\pipedel与普通的相同,对于和公司也\middle|相同。\big

类似地,每个规范都定义了放在结束符 前面的<size>的含义。当是时,这里有一个微妙的点。当 TeX 发现它用和打开一个组时\braketright\rangle<size>\left\left...\middle\left关闭重新\middle打开它,直到找到下一个\middle\right。所以我们必须将我们所做的重新定义延续到下一个组,这是通过奇怪的实现的\aftergroup。但这太过分了。

答案2

如果您愿意使用\left和的(略微)修改版本\right,您可以激活切换(布尔真/假变量)来跟踪您是否在\left-\right范围内。

在下面的 MWE 中,我定义了切换insidedelim,即true当您调用时\left*,并且在调用后变为 false - 它特定于和\right*的定义。嗯,实际上只有,但您可以以类似的方式定义其他宏,只要它们重复的结构(这会关闭切换)。进行这种小改动的原因是因为您有兴趣让分隔符根据其范围之外的内容进行扩展。常规和分隔符对仍然像往常一样工作。\bra\ket\ket\ketinsidedelim\left\right

在此处输入图片描述

\documentclass{article}
\usepackage{amsmath}% http://ctan.org/pkg/amsmath
\usepackage{etoolbox}% http://ctan.org/pkg/etoolbox
\usepackage{xparse}% http://ctan.org/pkg/xparse
\newtoggle{insidedelim}% 
\newcommand{\bra}[1]{% \bar{<stuff>}
  \iftoggle{insidedelim}%
    {\oldleft\langle #1 \middle|}% \left*
    {\langle #1|}% \langle #1 \mid
}
\newcommand{\ket}[1]{% \ket{<stuff>}
  \iftoggle{insidedelim}%
    {\middle| #1 \oldright\rangle\togglefalse{insidedelim}}% \right*
    {|#1 \rangle}% \mid #1 \rangle
}
\let\oldleft\left
\let\oldright\right
\RenewDocumentCommand{\left}{s}{%
  \IfBooleanTF{#1}% starred/unstarred
    {\toggletrue{insidedelim}}% \left*
    {\oldleft}% \left
}
\RenewDocumentCommand{\right}{s}{%
  \IfBooleanTF{#1}% starred/unstarred
    {}% \right*
    {\oldright}% \right
}
\begin{document}
$\langle x | \frac{\hat{p}}{m} | x \rangle$ \par \bigskip

$\bra{x} \frac{\hat{p}}{m} \ket{x}$ \par \bigskip

$\left\langle x \middle| \frac{\hat{p}}{m} \middle| x \right\rangle$ \par \bigskip

$\left*\bra{x} \frac{\hat{p}}{m} \right*\ket{x}$
\end{document}

切换和切换由etoolbox而高级命令定义接口由xparse

相关内容