具有可自定义分隔符的变体

具有可自定义分隔符的变体

我对 非常陌生expl3。我曾尝试编写一个命令来打印内积,采用逗号分隔的列表。如果列表仅包含一个元素,则打印 〈elem1, elem1〉,否则打印 〈elem1, ..., elemn〉。以下是代码

\documentclass{article}

\usepackage{mathtools}
\usepackage{xparse}

\DeclarePairedDelimiterX{\inner}[1]{\langle}{\rangle}{\innerp{#1}}

\ExplSyntaxOn

\tl_new:N \l_innerp_elem_int
\NewDocumentCommand{\innerp}{m}
{
  \tl_set:Nf \l_innerp_elem_int { \clist_count:n { #1 } }
  \int_compare:nNnTF { \l_innerp_elem_int } { = } { 1 }
  { \innerp_print_list:n { #1, #1 } }
  { \innerp_print_list:n { #1 }  }
}

\clist_new:N \l_innerp_arguments_clist
\cs_new_protected:Nn \innerp_print_list:n
{
  \clist_set:Nn \l_innerp_arguments_clist { #1 }
  \clist_use:Nn \l_innerp_arguments_clist { , }
}

\ExplSyntaxOff

\begin{document}
$\inner{\cdot}$, $\inner{v,w}$
\end{document}

到目前为止,它运行良好。我的问题是,这是否是惯用/正确的方法,或者有什么可以改进的?例如,如果使用包含两个以上元素的列表调用它,我想阻止/打印错误消息。

答案1

注意:此行类型不匹配:

\tl_new:N \l_innerp_elem_int

您的变量有_int后缀;因此应使用 进行声明\int_new:N。在这里,您声明了一个标记列表变量,其名称违反了惯例,让人以为它是一个整数变量。您很幸运它能正常工作,因为tl变量是作为非受保护的宏实现的,因此它们在整数表达式(例如 的第一个和第三个参数)中自然扩展\int_compare:nNnTF

出于同样的原因,\tl_set:Nf \l_innerp_elem_int {...}读取时是错误的,但实际上“没问题”,因为你的\l_innerp_elem_int是一个tl变量!

我认为后面\clist_set:Nn紧跟着的\clist_use:Nn不是必要的(见下面的代码)。最棘手的部分是错误消息的定义,因为我想打印\inner后面跟着的,:中间没有空格。这并不那么简单,因为:根据\ExplSyntaxOn制度(即,类别代码为 11)。定义此错误消息的另一种方法是:

\cs_generate_variant:Nn \msg_new:nnn { nne }

\msg_new:nne { innerp } { too-many-operands }
  { Too~many~operands~in~argument~'#1'~of~\c_backslash_str inner:~#2. }

-type参数e基于\expanded2019 年或更高版本的 TeX 引擎,与x-type 参数相反,它不需要将#标记加倍。

其余代码(主要是使用 的项数 switch-case \int_case:nnF)非常简单,如您所见。如其文档旁边的星号所示界面3.pdf\clist_count:n是完全可扩展的;这就是它在 的第一个参数中起作用的原因\int_case:nnF

\documentclass{article}
\usepackage{mathtools}
\usepackage{xparse}

\DeclarePairedDelimiterX{\inner}[1]{\langle}{\rangle}{\innerp{#1}}

\ExplSyntaxOn
% Replace 'innerp' with the name of your package (or something likely to be
% unique).
\msg_new:nnn { innerp } { too-many-operands }
  { Too~many~operands~in~argument~'#1'~of~\tl_to_str:n { \inner: } #2. }

\cs_generate_variant:Nn \msg_error:nnnn { nnnx }

\NewDocumentCommand \innerp { m }
  {
    \int_case:nnF { \clist_count:n {#1} }
      {
        { 1 } { #1, #1 }
        { 2 } {#1}
      }
      {
        \msg_error:nnnx { innerp } { too-many-operands }
          {#1} { \clist_count:n {#1} }
      }
  }
\ExplSyntaxOff

\begin{document}

$\inner{\cdot} - \inner{v,w} + \inner{u} = \inner{u,v}$

% Package innerp Error: Too many operands in argument 'u,v,w' of \inner: 3.
% $\inner{u,v,w}$

\end{document}

在此处输入图片描述

具有可自定义分隔符的变体

上述代码的以下变体稍微复杂一些,允许您在运行时使用第二个可选参数选择内部分隔符\inner(第一个可选参数给出分隔符大小 - 参见下面的第二个示例)。

\documentclass{article}
\usepackage{mathtools}
\usepackage{xparse}

\ExplSyntaxOn
% Replace 'innerp' with the name of your package (or something likely to be
% unique).
\msg_new:nnn { innerp } { too-many-operands }
  { Too~many~operands~in~argument~'#1'~of~\tl_to_str:n { \inner: } #2. }

\cs_generate_variant:Nn \msg_error:nnnn { nnnx }

% #1: separator used in the output
% #2: comma-list of items
\cs_new_protected:Npn \innerp_print_list:nn #1#2
  {
    \clist_set:Nn \l_tmpa_clist {#2}
    \clist_use:Nn \l_tmpa_clist {#1}
  }

\NewDocumentCommand \innerp { O{,} m }
  {
    \int_case:nnF { \clist_count:n {#2} }
      {
        { 1 } { \innerp_print_list:nn {#1} { #2, #2 } }
        { 2 } { \innerp_print_list:nn {#1} {#2} }
      }
      {
        \msg_error:nnnx { innerp } { too-many-operands }
          {#2} { \clist_count:n {#2} }
      }
  }

\DeclarePairedDelimiterX{\explicitInner}[2]{\langle}{\rangle}{\innerp[#1]{#2}}

\NewDocumentCommand \inner { O{} O{,} m }
  {
    \explicitInner [#1] {#2} {#3}
  }
\ExplSyntaxOff

\begin{document}

\[ \inner{\cdot} - \inner{v,w} + \inner{u} = \inner{u,v} \]

\[ \inner[][\delimsize\vert]{u,u} =
   \inner[\Big][\delimsize\vert]{\, \sum_{i=1}^{n} x_i e_i} \]

% Package innerp Error: Too many operands in argument 'u,v,w' of \inner: 3.
% $\inner{u,v,w}$

\end{document}

在此处输入图片描述

答案2

我意识到这是一个expl3疑问,但我认为我还是会添加传统的 2e 解决方案:

\documentclass{article}
\usepackage{listofitems}
\newcommand\inner[1]{\readlist\innerlist{#1}\langle%
  \ifnum\innerlistlen=1\relax#1,#1\else#1\fi\rangle}
\begin{document}
$\inner{\cdot}$, $\inner{v,w}$
\end{document}

在此处输入图片描述


补充

OP 在评论中要求提供错误捕获和指定输出分隔符的能力。在这里,后者是通过可选参数实现的。前者很容易实现。

\documentclass{article}
\usepackage{listofitems}
\newcommand\inner[2][,]{\readlist\innerlist{#2}\langle%
  \ifnum\innerlistlen=1\relax#2#1#2\else
  \ifnum\innerlistlen>2\textrm{Error!}\else
  \innerlist[1]#1\innerlist[2]\fi\fi\rangle}
\begin{document}
$\inner{\cdot}$, $\inner[|]{v,w}$, $\inner{v,w,z}$
\end{document}

在此处输入图片描述

答案3

由于您最多有两个参数,因此无需检查长度。\SplitArgument{1}{,}如果提供两个以上的项目,您将收到错误。

我还添加了一个简写形式,用简单的句点来表示空白。

\documentclass{article}

\usepackage{mathtools}
\usepackage{xparse}

\DeclarePairedDelimiterX{\inner}[1]{\langle}{\rangle}{\innerinner{#1}}

\ExplSyntaxOn

\tl_new:N \l_innerp_elem_tl
\NewDocumentCommand{\innerinner}{>{\SplitArgument{1}{,}}m}
 {
  % \SplitArgument passes two braced items; if only one item
  % is passed, the second braced item will pass the “novalue” test
  \euklid_inner:nn #1
 }

\cs_new_protected:Nn \euklid_inner:nn
 {
  \tl_if_novalue:nTF { #2 }
   {
    \__euklid_inner:nn { #1 } { #1 }
   }
   {
    \__euklid_inner:nn { #1 } { #2 }
   }
 }

\cs_new_protected:Nn \__euklid_inner:nn
 {
  \str_if_eq:nnTF { #1 } { . } { \,{\cdot}\, } { #1 }
  ,
  \str_if_eq:nnTF { #2 } { . } { {\cdot}\, } { #2 }
}

\ExplSyntaxOff

\begin{document}

$\inner{.}+\inner{.,w}+\inner{v,.}+\inner{v}+\inner{v,w}$

\bigskip

$\inner[\big]{.}+\inner[\Big]{.,w}+\inner[\bigg]{v,.}
+\inner[\Bigg]{v}+\inner*{\dfrac{v}{2},w}$

\end{document}

在此处输入图片描述

相关内容