我正在尝试编写一个宏,\tup
它可以有效地扩展它:
\tup{a | b | ... | z}
更改为:
\left\langle a ~\middle|~ b ~\middle|~ ... ~\middle|~ z \right\rangle
我尝试了以下操作,但它只放下第一个分隔符然后停止。
\documentclass[preview]{standalone}
\newcommand{\brak}[1]{\ensuremath{\left\langle#1\right\rangle}}
\makeatletter
\newcommand{\tup}[1]{%
\begingroup%
\@tempswafalse%
\def\@sep{~\middle|~}%
\edef\@tempa{#1|}%
\expandafter\brak\expandafter{\expandafter\@tuploop\@tempa\relax}%
\endgroup%
}
\def\@tuploop#1|#2\relax{%
\if@tempswa\@sep\else\@tempswatrue\fi{#1}
\begingroup
\ifx\relax#2\relax
\renewcommand{\next}{\endgroup}%
\else
\renewcommand{\next}{\endgroup\@tuploop#2\relax}%
\fi
\next
}
\makeatother
\begin{document}
$\tup{a|b|c}$
\end{document}
但是,如果我将定义更改\@sep
为,\def\@sep{~|~}
那么它就会“起作用”,并将管道字符放在
我认为\middle
扩展可能为时过早,但我不知道该怎么做。我该如何实现它?
答案1
答案2
作为一般规则,我建议尽可能\left
避免。\right
\DeclarePairedDelimiter
下面是一个与使用from定义的命令具有类似语法的实现mathtools
。
未修饰的命令使用正常大小;在可选参数中可以出现\big
、\Big
或。表示使用自动调整大小。\bigg
\Bigg
*
强制参数中的空格将被忽略,因此\tup{a|b|c}
与 相同\tup{a | b | c}
。
\documentclass{article}
\usepackage{amsmath}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\tup}{som}
{
\IfBooleanTF{#1}
{
\tl_set:Nn \l__reinking_tup_open_tl { \left\langle }
\tl_set:Nn \l__reinking_tup_middle_tl { \;\middle|\; }
\tl_set:Nn \l__reinking_tup_close_tl { \right\rangle }
}
{
\IfNoValueTF{#2}
{
\tl_set:Nn \l__reinking_tup_open_tl { \langle }
\tl_set:Nn \l__reinking_tup_middle_tl { \mathrel{|} }
\tl_set:Nn \l__reinking_tup_close_tl { \rangle }
}
{
\tl_set:Nn \l__reinking_tup_open_tl { \mathopen{#2\langle} }
\tl_set:Nn \l__reinking_tup_middle_tl { \mathrel{#2|} }
\tl_set:Nn \l__reinking_tup_close_tl { \mathclose{#2\rangle} }
}
}
\__reinking_tup:n { #3 }
}
\tl_new:N \l__reinking_tup_open_tl
\tl_new:N \l__reinking_tup_middle_tl
\tl_new:N \l__reinking_tup_close_tl
\seq_new:N \l__reinking_tup_items_seq
\cs_new_protected:Nn \__reinking_tup:n
{
\seq_set_split:Nnn \l__reinking_tup_items_seq { | } { #1 }
\l__reinking_tup_open_tl
\seq_use:NV \l__reinking_tup_items_seq \l__reinking_tup_middle_tl
\l__reinking_tup_close_tl
}
\cs_generate_variant:Nn \seq_use:Nn { NV }
\ExplSyntaxOff
\begin{document}
$\tup{a|b|\dots|z}$
\quad
$\tup[\big]{a|b|\dots|z}$
\quad
$\tup[\Big]{a|b|\dots|z}$
\quad
$\tup*{\dfrac{a}{2}|b|\dots|z}$
\end{document}
无软件包版本,它不加区别地使用\left
、\middle
和\right
。每次吸收一项并附加到列表中,然后执行该列表。
\documentclass{article}
\usepackage{amsmath}
\makeatletter
\newcommand{\tup}[1]{%
\def\@tup@list{}%
\@tup#1|\@tup|
}
\def\@tup#1|{%
\ifx\@tup#1\relax
\expandafter\@firstoftwo
\else
\expandafter\@secondoftwo
\fi
{\left\langle\@tup@list\right\rangle}%
{\ifx\@tup@list\@empty\@tup@append{#1}\else\@tup@append{\;\middle|\;#1}\fi\@tup}%
}
\def\@tup@append#1{%
\expandafter\def\expandafter\@tup@list\expandafter{\@tup@list#1}%
}
\makeatother
\begin{document}
$\tup{a}$
$\tup{a|b|\dots|z}$
\end{document}
这样,由\left
、\middle
和隐式形成的组就不会出现问题\right
。
答案3
你的命令是几乎正在工作。如果您将更多项目放入其中\tup
,则会$\tup{a|b|c|d|e|f}$
看到以下内容:
第一个项不是被遗漏的,而是成对分组的。您的命令以 开头\left\langle
,它启动一个“数学左组”,并将\@tempswa
开关设置为 false。在 的第一次迭代中,\@tuploop
您将执行\if@tempswa\@sep\else\@tempswatrue\fi
扩展为\@tempswatrue
,这意味着\@sep
现在没有插入,并且\@tempswa
现在为真。然后,该命令插入第一个项(a
)并继续进行下一次迭代。
在第二次迭代中,\@tempswa
为真并\if@tempswa\@sep\else\@tempswatrue\fi
扩展为\@sep
,插入\middle|
(后面的分隔符a
)。\middle
基元将结束由 开始的“数学左组” \left\langle
,并将开始另一个“数学左组”。但是,当第一个组结束时,您的\@tempswa
开关恢复为当前组之外的值,即 false!现在命令插入b
并转到第三次迭代,但现在\@tempswa
再次设置为 false。
因此,由于触发了组的结束,因此您的命令实际上是插入,<token>|<token>
然后等等。您可以使用一些魔法来规避这个问题,或者简单地对开关进行全局分配:<token>|<token>
\middle
\aftergroup
\if@tempswa\@sep\else\global\@tempswatrue\fi
但是,正如 egreg 在评论中所说,\@tempswa
应该仅在本地设置,因此最好创建一个\newif\ifg@insertsep
然后始终使用\global\g@insertseptrue
和\global\g@insertsepfalse
。
与此同时,由于 egreg 发布的expl3
答案比我发布的单行答案更为详细,因此我将在这里停止;-)