允许参数中出现零次或一次字符串(但不能更多)

允许参数中出现零次或一次字符串(但不能更多)

在以下 MWE 中,我尝试捕获\at拆分输入参数的特定字符串(在此字符串中)。我想使用解析器来允许\at参数中不存在的情况,但不允许出现两次或多次\at

\documentclass{article}

\makeatletter
\long\def\@split#1\at#2\@endsplit{#1 -- #2}
\long\def\split#1{\@split#1\@endsplit}
\makeatother


\begin{document}

\split{Hello \at \[ A \] \paragraph{Testing}

  \textit{worlds}

  Working!
}

% This is also OK
\split{\split{ Hello \at another} \at \split{ something \at World}}

% % How to allow this form producing "Hello ???" for example
% \split{Hello}

% % and issue error for this one
% \split{Hello \at another \at World}
\end{document}

我无法找到一种不定义的方法,\at这在我的例子中是有问题的,因为我认为我必须在一个组中进行嵌套\split,但我需要避免创建多余的组。我也可以测试参数是否为空,但参数不能像上一个示例那样长(我认为)。

答案1

您可以使用xparse的参数处理器来管理您的请求:

在此处输入图片描述

\documentclass{article}

\NewDocumentCommand{\printsplit}{ +m +m }{%
    #1%
    \IfValueT{#2}{~--~#2}}
\NewDocumentCommand{\split}{ > {\SplitArgument{1}{\at}} +m }{%
  \printsplit #1}

\begin{document}

\split{Hello \at \[ A \] \paragraph{Testing}

  \textit{worlds}

  Working!
}

% This is also OK
\split{\split{ Hello \at another} \at \split{ something \at World}}

\split{Hello}

%% and issue error for this one
%\split{Hello \at another \at World}

\end{document}

> {\SplitArgument{1}{\at}} +m允许“长” mandatory 参数(带有agraph)最多\par拆分一次。因此,您可能希望没有拆分,或者只有一个拆分。在前一种情况下,传递给的第二个参数将是可以使用条件测试的。\at1\printsplit-NoValue-\IfValueT{#2}

答案2

在 处拆分你的参数\at。这只会\at在外层找到,因此\split可以嵌套。

这个名字\split选得不好,因为amsmath如果你定义了它,你就不能加载它。或者如果你加载了它,你就不能定义它amsmath。但对于这个例子来说,这是可以的。

\documentclass{article}

\ExplSyntaxOn

\NewDocumentCommand{\split}{+m}
 {
  \tohiko_split:n { #1 }
 }

\seq_new:N \l__tohiko_split_seq

\cs_new_protected:Nn \tohiko_split:n
 {
  \seq_set_split:Nnn \l__tohiko_split_seq { \at } { #1 }
  \int_case:nnF { \seq_count:N \l__tohiko_split_seq }
   {
    {0}{--}
    {1}{\seq_item:Nn \l__tohiko_split_seq { 1 } ~ -- ~ ??}
    {2}{\seq_item:Nn \l__tohiko_split_seq { 1 } ~ -- ~ \seq_item:Nn \l__tohiko_split_seq { 2 }}
   }
   {\msg_error:nn { tohiko/split } { too-many-at }}
 }

\msg_new:nnnn { tohiko/split } { too-many-at }
 {Too~many~\token_to_str:N \at\space tokens}
 {You~have~too~many~\token_to_str:N \at\space in~the~argument~to~split}

\ExplSyntaxOff

\begin{document}

\split{Hello \at \[ A \] \paragraph{Testing}

  \textit{worlds}

  Working!
}

% This is also OK
\split{\split{ Hello \at another} \at \split{ something \at World}}

% % How to allow this form producing "Hello ???" for example
\split{Hello}

% % and issue error for this one
\split{Hello \at another \at World}

\end{document}

控制台输出:

This is pdfTeX, Version 3.141592653-2.6-1.40.24 (TeX Live 2022) (preloaded format=pdflatex)
 restricted \write18 enabled.
entering extended mode
(./splitat.tex
LaTeX2e <2022-11-01> patch level 1
L3 programming layer <2023-02-07>
(/usr/local/texlive/2022/texmf-dist/tex/latex/base/article.cls
Document Class: article 2022/07/02 v1.4n Standard LaTeX document class
(/usr/local/texlive/2022/texmf-dist/tex/latex/base/size10.clo))
(/usr/local/texlive/2022/texmf-dist/tex/latex/l3backend/l3backend-pdftex.def)
(./splitat.aux)

! Package tohiko/split Error: Too many \at tokens

For immediate help type H <return>.
 ...

l.46 \split{Hello \at another \at World}

? h

You have too many \at in the argument to split

?
[1{/usr/local/texlive/2022/texmf-var/fonts/map/pdftex/updmap/pdftex.map}]
(./splitat.aux) )</usr/local/texlive/2022/texmf-dist/fonts/type1/public/amsfont
s/cm/cmbx10.pfb></usr/local/texlive/2022/texmf-dist/fonts/type1/public/amsfonts
/cm/cmmi10.pfb></usr/local/texlive/2022/texmf-dist/fonts/type1/public/amsfonts/
cm/cmr10.pfb></usr/local/texlive/2022/texmf-dist/fonts/type1/public/amsfonts/cm
/cmti10.pfb>
Output written on splitat.pdf (1 page, 43559 bytes).
Transcript written on splitat.log.

在此处输入图片描述

答案3

也许这可以实现您想要的功能 — — 而不是\split命名命令,\SplitAt以避免与 amsmath-stuff 发生名称冲突:

\errorcontextlines=10000
\makeatletter
%%=============================================================================
%% PARAPHERNALIA:
%% \UD@firstoftwo, \UD@secondoftwo, \UD@stopromannumeral, \UD@CheckWhetherNull
%%=============================================================================
\newcommand\UD@firstoftwo[2]{#1}%
\newcommand\UD@secondoftwo[2]{#2}%
\@ifdefinable\UD@stopromannumeral{\chardef\UD@stopromannumeral=`\^^00}%
%%-----------------------------------------------------------------------------
%% 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]{%
  \romannumeral\expandafter\UD@secondoftwo\string{\expandafter
  \UD@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
  \UD@secondoftwo\string}\expandafter\UD@firstoftwo\expandafter{\expandafter
  \UD@secondoftwo\string}\expandafter\UD@stopromannumeral\UD@secondoftwo}{%
  \expandafter\UD@stopromannumeral\UD@firstoftwo}%
}%
%%=============================================================================
\@ifdefinable\UD@GobbleToSecondAt{\long\def\UD@GobbleToSecondAt#1\at#2\at{}}%
\@ifdefinable\UD@AtSplit{\long\def\UD@AtSplit#1\at{#1 -- }}%
%You might prefer:
%\@ifdefinable\UD@AtSplit{\long\def\UD@AtSplit#1\at{#1-- }}%
\@ifdefinable\SplitAt{%
  \DeclareRobustCommand\SplitAt[1]{%
    \romannumeral
    \expandafter\UD@CheckWhetherNull\expandafter{\UD@GobbleToSecondAt#1\at\at}{\UD@AtSplit\UD@stopromannumeral#1\at???}{%
      \expandafter\UD@CheckWhetherNull\expandafter{\UD@GobbleToSecondAt#1\at}{\UD@AtSplit\UD@stopromannumeral#1}{%
        \UD@stopromannumeral
        \GenericError{}{Error: Too many \string\at\space tokens}{You have too many \string\at\space tokens in the argument to split.}{}%
      }%
    }%
  }%
}%
\makeatother

\documentclass{article}

\begin{document}

\SplitAt{Hello \at \[ A \] \paragraph{Testing}

  \textit{worlds}

  Working!
}

% This is also OK
\SplitAt{\SplitAt{ Hello \at another} \at \SplitAt{ something \at World}}

% % How to allow this form producing "Hello ???" for example
\SplitAt{Hello}

\message{^^J!!! The next error-message is what is wanted: !!!^^J}%
% % and issue error for this one
\SplitAt{Hello \at another \at World}
\end{document}

在此处输入图片描述

注意输出的倒数第二行:

您可以看到,每个破折号周围都有两个空格,而不是一个空格,因为该命令既不会删除空格标记,也不会删除要分开的组件周围的花括号,而是\UD@AtSplit插入围绕破折号的空格标记。
也许您不想在破折号周围插入额外的空格。在这种情况下,请执行以下操作:
\@ifdefinable\UD@AtSplit{\long\def\UD@AtSplit#1\at{#1-- }}%

相关内容