假设我想编写嵌套方程,其中包含相当多的嵌套括号/分隔符,例如()
、[]
、{}
、||
,也许还有其他。还假设我认为如果外部分隔符比内部分隔符稍大(如果可能),这些内容看起来会更好,看起来像是“框住”了它们。
我可以写如下内容:
$\pi_G\big(f\big(\big[[v]_{\sim}\big]_{\sim''}\big)\big)$
但这些\big(...\big)
东西有点丑陋,妨碍了从 LaTeX 代码中看到发生了什么。如果我可以写得更好:
$\pi_G\mybig(){f\mybig(){\mybig[]{[v]_{\sim}}_{\sim''}}}$
这里的()
after\mybig
表示宏参数应该被 包围\big(...\big)
。如果是[]
,那么参数应该被 包围\big[...\big]
,依此类推。
但等等!这实际上是可以做到的;只需要定义带有三个参数的宏,如下所示:
\newcommand{\mybig}[3]{\big{#1}#3\big{#2}}
真好。但不幸的是,当我想使用括号时,这种方法不太好用,比如
$\mybig{}{(x)}$
这里,{}
实际上是一个组,并且只构成一个参数。所以这失败了。当然,以下两种方法都有效,但意味着宏的使用在外观上不太统一(这不太令人愉快):
$\mybig{}{}{(x)}$
$\mybig\{\}{(x)}$
所以我真正需要的是测试参数 #1 是否为空(表示宏调用后紧接着一个空组,即{}
)。如果是,则宏“跳”到第三个参数,并输入\big\{#3\big\}
。有办法做到这一点吗?
答案1
这是另一个 LaTeX3 版本。
\documentclass{article}
%\url{http://tex.stackexchange.com/q/131136/86}
\usepackage{xparse}
\ExplSyntaxOn
\cs_new_nopar:Npn \paren_set:nnnn #1#2#3#4
{
\use:c {#1} #2 #4 \use:c {#1} #3
}
\cs_new_nopar:Npn \paren_get_first:nn #1#2
{
\tl_if_empty:nTF {#1}
{
\paren_set:nnnn {big} {\lbrace} {\rbrace} {#2}
}
{
\tl_if_single:nTF {#1}
{
\paren_set:nnnn {big} {#1} {#2}
}
{
\tl_if_empty:nTF {#2}
{
\paren_set:nnnn {#1} {\lbrace} {\rbrace}
}
{
\paren_set:nnnn {#1} {#2}
}
}
}
}
\DeclareDocumentCommand \MyBig { }
{
\paren_get_first:nn
}
\ExplSyntaxOff
\begin{document}
\[
\MyBig(){\frac12} \quad
\MyBig[]{\frac12} \quad
\MyBig{}{\frac12} \quad
\MyBig(.{\frac12} \quad
\MyBig\{.{\frac12} \quad
\MyBig{Big}(){\frac12} \quad
\MyBig{bigg}[]{\frac12} \quad
\MyBig{Bigg}{}{\frac12} \quad
\MyBig(.{\frac12} \quad
\MyBig\{.{\frac12}
\]
\end{document}
这使用以下逻辑:
排版命令接受四个参数:大小、左分隔符、右分隔符和内容。调度程序必须根据输入确定这些参数应该是什么。选项如下:
\MyBig{}{Content}
. 用于big
大小\lbrace
,,\rbrace
用于分隔符。\MyBig(){Content}
. 用于big
大小(
,,)
用于分隔符。\MyBig{bigg}{}{Content}
. 用于bigg
大小\lbrace
,,\rbrace
用于分隔符。\MyBig{bigg}(){Content}
. 用于bigg
大小(
,,)
用于分隔符。
因此,调度程序执行以下操作:
- 读入两个参数。这是安全的,因为总是至少有两个。
- 检查第一个。它是空的吗?如果是,则命令一定是,
\MyBig{}{Content}
所以我们将其输入为\big\{<Content>\big\}
。到此结束。 - 如果第一个不为空,我们测试它是否是单例。如果是,参数一定是分隔符。第二个将是结束分隔符,我们还没有获取内容。所以我们用 调用排版器,
{big}{#1}{#2}
它将从流中获取内容。 - 如果第一个不是单例,则它是一个大小参数。因此第二个是分隔符,所以我们对其进行测试。如果它为空,我们的分隔符是括号,因此我们用 调用排版程序
{#1}{\lbrace}{\rbrace}
,下一组是内容。 - 现在的情况是,第一个不是单例,第二个不是空的。所以第二个是分隔符,我们必须从流中获取结束分隔符和内容,所以我们调用 typesetter
{#1}{#2}
并让它从流中获取其余内容。
请注意,分隔符不必匹配,但如果您想要不匹配的括号,则必须使用\{
或\}
(或\lbrace
或\rbrace
)。
答案2
使用 LaTeX3,xparse
以及一些技巧(已解释),效果是完全可以实现的。
\MyBig
我们设置了一个名为(为了符合当前惯例,这里大写)的文档级接口宏。此宏可以采用三个参数:
<>
一个可选参数,以,分隔- 一个“正常”的“可选参数”,用于测试是否需要
[]
分隔符,以及 - 一个强制性参数,用于检查我们是否传递了一个空参数,表示需要
{}
分隔符。
因此,文档级宏的唯一目的是分派给其他函数,进一步扫描输入流以查找所需的标记 - 分派器决定哪个宏获取输入流,宏决定它需要多少个标记。(我不相信有任何方法可以严格地拥有一个要求更多标记的开关,如果有,那肯定不漂亮。)
\documentclass{article}
\usepackage{expl3,xparse}
\ExplSyntaxOn
\msg_new:nnnn { mybig } { invalid-argument }
{ Argument~ `#1`~ invalid. }
{ The~ apparent~ optional~ argument~ should~ never~ take~ a~ value;~
it~ is~ solely~ for~ the~ purpose~ of~ delimiter~ specification.}
% Typeset, in braces of size #1, the content in #2
\cs_new:Npn \MyBig_braces #1 #2
{
#1\{ #2 #1\}
}
% Typeset, in brackets of size #1, the content in #2
\cs_new:Npn \MyBig_brackets #1 #2
{
#1[ #2 #1]
}
% Typeset, in delimiters #2 and #3 of size #1, the content in #4
\cs_new:Npn \MyBig_other #1 #2 #3 #4
{
#1#2 #4 #1#3
}
% If the (second) optional argument is empty, we were passed the
% delimiters [], so dispatch. If the optional argument is not empty
% and has a value, then something went horribly wrong. Otherwise
% (NoValue), check to see if the mandatory argument is empty. If it
% is, we want the {} delimiter, so dispatch. Otherwise, dispatch the
% generic handler.
\NewDocumentCommand \MyBig { D<>{\Big} o m }
{
\str_if_eq_x:nnTF { #2 } { }
{ \MyBig_brackets { #1 } { #3 } }
{
\IfValueTF { #2 }
{ \msg_error:nnn { mybig } { invalid-argument } { #2 } }
{
\str_if_eq_x:nnTF { #3 } { }
{ \MyBig_braces { #1 } }
{ \MyBig_other { #1 } { #3 } }
}
}
}
\ExplSyntaxOff
\begin{document}
\[
\MyBig(){\frac12} \quad
\MyBig[]{\frac12} \quad
\MyBig{}{\frac12} \quad
\MyBig<\big>{}{\frac12} \quad
\MyBig<\Bigg>[]{\MyBig{}{\int_0^\infty}}
\]
% \[ \MyBig<\Bigg>[bad]{\MyBig{}{\int_0^\infty}} \]
\end{document}
答案3
您可以通过测试是否存在带星号形式的命令来执行此类操作,在这种情况下会使用\{
和。\}
\makeatletter
\def\mybig{\@ifstar{\@mybig}{\@@mybig}}
\def\@mybig#1{\left\{#1\right\}}
\def\@@mybig#1#2#3{\left#1#3\right#2}
\makeatother
然后是以下代码
\[\mybig[]{abc}\]
\[\mybig*{\mybig{\langle}{\rangle}{abc}}\]
\[\mybig.{|}{\int_a^b 2x\mathrm{d}x = x^2}_{a}^{b}\]
会产生类似
更新
下面是一些测试空参数的代码。
\documentclass{article}
\makeatletter
\def\mybig#1{%%"
\def\@my@temp{#1}%%
\ifx\@my@temp\@empty\expandafter\@mybig\else
\expandafter\@@mybig\expandafter\@my@temp\fi}
\def\@mybig#1{\left\{#1\right\}}
\def\@@mybig#1#2#3{\left#1#3\right#2}
\makeatother
\begin{document}
\[\mybig[]{abc}\]
\[\mybig{}{\mybig{\langle}{\rangle}{abc}}\]
\[\mybig[]{ \int_a^b 2x\mathrm{d}x }^2\]
\end{document}
但这种事情并不是万无一失的!
答案4
您可以使用一个简单的条件来做到这一点,测试第一个参数是否为空(另一件事是这是否会产生良好的印刷效果;例如,在下面的第二个代码中,实际上不需要增加大小分隔符):
\documentclass{article}
\usepackage{amsmath}
\newcommand\mybig[3]{%
\if\detokenize{#1}\relax\relax
\bigl\{#3\bigr\}
\else
\bigl#1#3\bigr#2
\fi}
\begin{document}
$\mybig{\lvert}{\rvert}{\lvert x-y\rvert}$
$\mybig{}{}{\lvert x-y\rvert}$
\end{document}