具有可变数量参数的命令:逗号与和

具有可变数量参数的命令:逗号与和

我有以下命令:\newcommand{triangles}[2]{triangles $\triangle #1$ and $\triangle #2},这样会\triangles{ABC}{DEF}产生triangles $\triangle ABC$ and $\triangle DEF$。我想扩展该命令,使其可以接受任意数量的参数,并且产生\triangles{ABC}{DEF}{GHI}{JKL}triangles $\triangle ABC$, $\triangle DEF$, $\triangle GHI$ and $\triangle DEF$我曾在某处看到过一个购物清单的解决方案,它利用了\makeatletter和一些 TeX 命令,但我认为这更困难,因为在添加当前参数之前,您需要知道它是否是最后一个参数(如果不是:请输入逗号,如果是:请输入“and”)。

答案1

您正在讨论的购物清单位于编写一个接受可变数量参数的 LaTeX 宏

您可以快速调整它以处理任意数量的三角形。还处理只有一个三角形的情况,以便仅显示“三角形”而不是“三角形”。

\documentclass[11pt]{article}

\makeatletter
    \newcommand{\triangles}[1]{%
        \checknextarg{#1}}
    \newcommand{\checknextarg}[1]{%
        \@ifnextchar\bgroup{Triangles $\triangle #1$\gobblenextarg}{Triangle $\triangle #1$}}
    \newcommand{\gobblenextarg}[1]{%
        \@ifnextchar\bgroup{, $\triangle #1$\gobblenextarg}{ and $\triangle #1$}}
\makeatother

\begin{document}

\triangles{ABC}

\triangles{ABC}{DEF}

\triangles{ABC}{DEF}{GHI}

\triangles{ABC}{DEF}{GHI}{JKL}

\end{document}

这是得到的结果:

三角形

编辑

根据以下评论乌尔里希·迪茨,此命令的改进版本为:

\documentclass[11pt]{article}

\usepackage{ltxcmds}

\makeatletter
    \newcommand{\triangles}[1]{%
        \checknextarg{#1}}
    \newcommand{\checknextarg}[1]{%
        \ltx@ifnextchar@nospace\bgroup{Triangles $\triangle #1$\gobblenextarg}{Triangle $\triangle #1$}}
    \newcommand{\gobblenextarg}[1]{%
        \ltx@ifnextchar@nospace\bgroup{, $\triangle #1$\gobblenextarg}{ and $\triangle #1$}}
\makeatother

\begin{document}

\triangles{ABC}

\triangles{ABC}{DEF}

\triangles{ABC}{DEF}{GHI}

\triangles{ABC}{DEF}{GHI}{JKL}

\triangles{ABC}{DEF}{GHI}{JKL} {\Large This shall not be part of the command}

\end{document}

where\ltx@ifnextchar@nospace不跳过空格并阻止命令后的文本被解释为命令的“参数” \triangles

然而,正如egreg 的回答,应该避免这种做事方式。

答案2

具有可变数量参数的宏不是使用 LaTeX 的最佳选择;对于您来说,使用以逗号分隔的三角形列表更简单。

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn

\NewDocumentCommand{\triangles}{m}
 {
  \bart_triangles:n { #1 }
 }

\seq_new:N \l__bart_triangles_seq
\tl_new:N \l__bart_triangles_item_tl

\cs_new_protected:Nn \bart_triangles:n
 {
  \seq_clear:N \l__bart_triangles_seq
  \clist_map_variable:nNn { #1 } \l__bart_triangles_item_tl
   {
    \__bart_triangles_add:V \l__bart_triangles_item_tl
   }
  triangle
  \int_compare:nF { \seq_count:N \l__bart_triangles_seq < 2 } { s }
  \nobreakspace
  \seq_use:Nnnn \l__bart_triangles_seq { ~and~ } { ,~ } { ~and~ }
 }

\cs_new_protected:Nn \__bart_triangles_add:n
 {
  \seq_put_right:Nn \l__bart_triangles_seq { $\triangle #1$ }
 }
\cs_generate_variant:Nn \__bart_triangles_add:n { V }

\ExplSyntaxOff

\begin{document}

\triangles{ABC}

\triangles{ABC,DEF}

\triangles{ABC,DEF,GHI}

\end{document}

逗号分隔的列表被映射,并且每个项目都以 的形式添加到序列中$\triangle<vertices>$。然后使用该序列,并使用指定的分隔符(两个之间、两个以上之间、最后两个之间)。

仅当列表包含至少两个项目时才会添加“s”。

在此处输入图片描述

答案3

一种listofitems方法。注意:我被教导说,对于超过 2 个项目的列表,在“and”前面有一个逗号。

\documentclass{article}
\usepackage{listofitems}
\newcommand\triangles[1]{%
  \setsepchar{,}%
  \readlist\trilist{#1}%
  \foreachitem\x\in\trilist[]{%
    $\triangle\x$%
    \ifnum\numexpr\xcnt+1<\listlen\trilist[]\relax%
      ,\ %
    \else%
      \ifnum\numexpr\xcnt+1=\listlen\trilist[]\relax%
        \ifnum\listlen\trilist[]>2\relax,\fi
        \ and\ %
      \fi
    \fi%
  }%
}
\begin{document}
Given \triangles{ABC}.\par
Given \triangles{ABC, DEF}.\par
Given \triangles{ABC, DEF,GHI}.\par
Given \triangles{ABC, DEF,GHI, JKL}.\par
\end{document}

在此处输入图片描述

答案4

关于什么
\triangles{ABC}{DEF}{GHI}{JKL} {\large This shall not be part of the triangle-command}

为了明确哪些括号组属于\triangles哪些不属于,我强烈推荐类似这样的语法\triangles{{ABC}{DEF}{GHI}}

或者完全没有括号组的语法,如 egreg 所建议的那样,\triangles{ABC,DEF,GHI}

下面的例子展示了一种\romannumeral基于扩展的语法实现方式\triangles{{ABC}{DEF}{GHI}}

\documentclass{article}

\makeatletter
%%----------------------------------------------------------------------
\newcommand\UD@gobble[1]{}%
\newcommand\UD@firstoftwo[2]{#1}%
\newcommand\UD@secondoftwo[2]{#2}%
\newcommand\UD@exchange[2]{#2#1}%
\newcommand\UD@PassFirstToSecond[2]{#2{#1}}%
%%-----------------------------------------------------------------------------
%% Extract first inner undelimited argument:
%%.............................................................................
%%   \UD@ExtractFirstArg{ABCDE} yields  A
%%
%%   \UD@ExtractFirstArg{{AB}CDE} yields  AB
%%
%% !!! The argument of \UD@ExtractFirstArg must not be empty. !!!
%% You can check for emptiness via \UD@CheckWhetherNull before applying
%% \UD@ExtractFirstArg.
%% The argument of \UD@ExtractFirstArg may contain the token \UD@SelDOm.
%% 
%%.............................................................................
\newcommand\UD@RemoveTillUD@SelDOm{}%
\long\def\UD@RemoveTillUD@SelDOm#1#2\UD@SelDOm{{#1}}%
\newcommand\UD@ExtractFirstArg[1]{%
  \romannumeral0%
  \UD@ExtractFirstArgLoop{#1\UD@SelDOm}%
}%
\newcommand\UD@ExtractFirstArgLoop[1]{%
  \expandafter\UD@CheckWhetherNull\expandafter{\UD@firstoftwo{}#1}%
  {\UD@exchange#1{ }}%
  {\expandafter\UD@ExtractFirstArgLoop\expandafter{\UD@RemoveTillUD@SelDOm#1}}%
}%
%%----------------------------------------------------------------------
%% Check whether argument is empty:
%%......................................................................
%% \UD@CheckWhetherNull{<Argument which is to be checked>}%
%%                     {<Tokens to be delivered in case that argument
%%                       which is to be checked is empty>}%
%%                     {<Tokens to be delivered in case that argument
%%                       which is to be checked is not empty>}%
%% The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
%%   <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
\newcommand\UD@CheckWhetherNull[1]{%
  \romannumeral0\expandafter\UD@secondoftwo\string{\expandafter
  \UD@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
  \UD@secondoftwo\string}\expandafter\UD@firstoftwo\expandafter{\expandafter
  \UD@secondoftwo\string}\expandafter\expandafter\UD@firstoftwo{ }{}%
  \UD@secondoftwo}{\expandafter\expandafter\UD@firstoftwo{ }{}\UD@firstoftwo}%
}%
%%------------------------------------------------------------------------------
%% Check whether argument is blank (empty or only spaces):
%%..............................................................................
%% -- Take advantage of the fact that TeX discards space tokens when
%%    "fetching" _un_delimited arguments: --
%% \UD@CheckWhetherBlank{<Argument which is to be checked>}%
%%                      {<Tokens to be delivered in case that
%%                        argument which is to be checked is blank>}%
%%                      {<Tokens to be delivered in case that argument
%%                        which is to be checked is not blank}%
\newcommand\UD@CheckWhetherBlank[1]{%
  \romannumeral\expandafter\expandafter\expandafter\UD@secondoftwo
  \expandafter\UD@CheckWhetherNull\expandafter{\UD@firstoftwo#1{}.}%
}%
%%------------------------------------------------------------------------------
%% The triangles-list:
%%..............................................................................
\newcommand\triangles[1]{%
  \romannumeral0\UD@CheckWhetherBlank{#1}{ no triangle}{\triangleloop{#1}{}}%
}%
\newcommand\triangleloop[2]{%
  \UD@CheckWhetherBlank{#1}{ #2}{%
    \expandafter\UD@PassFirstToSecond\expandafter{%
      \romannumeral0\expandafter\UD@exchange\expandafter
      {%
        \romannumeral0\expandafter\expandafter\expandafter\UD@exchange
        \expandafter\expandafter\expandafter{\UD@ExtractFirstArg{#1}$}{ $\triangle}%
      }{%
        \expandafter\UD@CheckWhetherBlank\expandafter{\UD@gobble#1}{%
           \UD@CheckWhetherBlank{#2}{ triangle }{ triangles #2 and }%
        }{%
           \UD@CheckWhetherBlank{#2}{ }{ #2, }%
        }%
      }%
    }{%
      \expandafter\triangleloop\expandafter{\UD@gobble#1}%
    }%
  }%
}%
\makeatother


\begin{document}

\triangles{}\par
\triangles{{ABC}}\par
\triangles{{ABC} {DEF} {GHI}}\par
\triangles{{ABC} {DEF}}\par
\triangles{{ABC} {DEF} {GHI} {JKL}}\par

\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\ThisTrianglelist
\expandafter\expandafter\expandafter{%
  \triangles{{ABC} {DEF} {GHI} {JKL}}%
}%

\texttt{\frenchspacing|\string\ThisTrianglelist=\meaning\ThisTrianglelist|}

\end{document}

在此处输入图片描述


当列表包含两个以上的项目时,如果您希望在“and”前加一个逗号:

\documentclass{article}

\makeatletter
%%----------------------------------------------------------------------
\newcommand\UD@gobble[1]{}%
\newcommand\UD@firstoftwo[2]{#1}%
\newcommand\UD@secondoftwo[2]{#2}%
\newcommand\UD@exchange[2]{#2#1}%
\newcommand\UD@PassFirstToSecond[2]{#2{#1}}%
%%-----------------------------------------------------------------------------
%% Extract first inner undelimited argument:
%%.............................................................................
%%   \UD@ExtractFirstArg{ABCDE} yields  A
%%
%%   \UD@ExtractFirstArg{{AB}CDE} yields  AB
%%
%% !!! The argument of \UD@ExtractFirstArg must not be empty. !!!
%% You can check for emptiness via \UD@CheckWhetherNull before applying
%% \UD@ExtractFirstArg.
%% The argument of \UD@ExtractFirstArg may contain the token \UD@SelDOm.
%% 
%%.............................................................................
\newcommand\UD@RemoveTillUD@SelDOm{}%
\long\def\UD@RemoveTillUD@SelDOm#1#2\UD@SelDOm{{#1}}%
\newcommand\UD@ExtractFirstArg[1]{%
  \romannumeral0%
  \UD@ExtractFirstArgLoop{#1\UD@SelDOm}%
}%
\newcommand\UD@ExtractFirstArgLoop[1]{%
  \expandafter\UD@CheckWhetherNull\expandafter{\UD@firstoftwo{}#1}%
  {\UD@exchange#1{ }}%
  {\expandafter\UD@ExtractFirstArgLoop\expandafter{\UD@RemoveTillUD@SelDOm#1}}%
}%
%%----------------------------------------------------------------------
%% Check whether argument is empty:
%%......................................................................
%% \UD@CheckWhetherNull{<Argument which is to be checked>}%
%%                     {<Tokens to be delivered in case that argument
%%                       which is to be checked is empty>}%
%%                     {<Tokens to be delivered in case that argument
%%                       which is to be checked is not empty>}%
%% The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
%%   <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
\newcommand\UD@CheckWhetherNull[1]{%
  \romannumeral0\expandafter\UD@secondoftwo\string{\expandafter
  \UD@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
  \UD@secondoftwo\string}\expandafter\UD@firstoftwo\expandafter{\expandafter
  \UD@secondoftwo\string}\expandafter\expandafter\UD@firstoftwo{ }{}%
  \UD@secondoftwo}{\expandafter\expandafter\UD@firstoftwo{ }{}\UD@firstoftwo}%
}%
%%------------------------------------------------------------------------------
%% Check whether argument is blank (empty or only spaces):
%%..............................................................................
%% -- Take advantage of the fact that TeX discards space tokens when
%%    "fetching" _un_delimited arguments: --
%% \UD@CheckWhetherBlank{<Argument which is to be checked>}%
%%                      {<Tokens to be delivered in case that
%%                        argument which is to be checked is blank>}%
%%                      {<Tokens to be delivered in case that argument
%%                        which is to be checked is not blank}%
\newcommand\UD@CheckWhetherBlank[1]{%
  \romannumeral\expandafter\expandafter\expandafter\UD@secondoftwo
  \expandafter\UD@CheckWhetherNull\expandafter{\UD@firstoftwo#1{}.}%
}%
%%------------------------------------------------------------------------------
%% The triangles-list:
%%..............................................................................
\newcommand\triangles[1]{%
  \romannumeral0\UD@CheckWhetherBlank{#1}{ no triangle}{\triangleloop{#1}{}{}}%
}%
\newcommand\triangleloop[3]{%
  \UD@CheckWhetherBlank{#1}{ #2}{%
    \expandafter\UD@exchange\expandafter{%
      \romannumeral0\UD@exchange{ }{\expandafter\expandafter\expandafter
      \expandafter\expandafter\expandafter\expandafter}%
      \expandafter\UD@exchange\expandafter{%
         \romannumeral0%
          \expandafter\UD@CheckWhetherBlank\expandafter{\UD@gobble#1}{ {}}{%
             \UD@CheckWhetherBlank{#2}{ {}}{ {,}}%
          }%
      }{\expandafter{%
        \romannumeral0\expandafter\UD@exchange\expandafter
        {%
          \romannumeral0\expandafter\expandafter\expandafter\UD@exchange
          \expandafter\expandafter\expandafter{\UD@ExtractFirstArg{#1}$}{ $\triangle}%
        }{%
          \expandafter\UD@CheckWhetherBlank\expandafter{\UD@gobble#1}{%
             \UD@CheckWhetherBlank{#2}{ triangle }{ triangles #2#3 and }%
          }{%
             \UD@CheckWhetherBlank{#2}{ }{ #2, }%
          }%
        }%
      }}%
    }{%
      \expandafter\triangleloop\expandafter{\UD@gobble#1}%
    }%
  }%
}%
\makeatother


\begin{document}

\triangles{}\par
\triangles{{ABC}}\par
\triangles{{ABC} {DEF}}\par
\triangles{{ABC} {DEF} {GHI}}\par
\triangles{{ABC} {DEF} {GHI} {JKL}}\par

\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\ThisTrianglelist
\expandafter\expandafter\expandafter{%
  \triangles{{ABC} {DEF} {GHI} {JKL}}%
}%

\texttt{\frenchspacing|\string\ThisTrianglelist=\meaning\ThisTrianglelist|}

\end{document}

在此处输入图片描述

相关内容