检查下一个字符时不考虑空格

检查下一个字符时不考虑空格

取自这个帖子,我有以下代码来定义一个宏,该宏采用任意数量的参数并\simeq_{k}在数学模式下将它们与交错符号()连接起来:

\makeatletter
\def\kleene{%
  %
  \futurelet\@let@token\@kleene
}
\def\@kleene{%
  \ifx\@let@token\bgroup
    \expandafter\@@kleene
  \else
    \expandafter%
  \fi
}
\def\@@kleene#1{%
  #1%
  \futurelet\@let@token\@@@kleene
}
\def\@@@kleene{%
  \ifx\@let@token\bgroup
    \simeq_{k}
    \expandafter\@@kleene
  \else
    \expandafter%
  \fi
}
\makeatother

顺便说一句,我使用乳胶奥特并且的参数\kleene可能为 的形式[[some_ott_syntax]]。不幸的是,双分隔符[[]]是为在我的 latex 代码中包含 Ott 代码而保留的。因此,大多数情况下,的参数\kleene包含这些分隔符,我无法避免这种情况。

该宏基于检查下一个字符是否为{。这迫使我把每个 参数都\kleene放在前一个参数的旁边,中间不留空格,就像我下面展示的那样。请注意,参数可能采用 的形式[[arg]](因为它们是 Ott 代码,所以用 包围[[]])。

\kleene{arg1}{arg2}{arg3}{arg4}

但是,有时我想传递一长串参数,而每个参数本身可能都相当长。为了提高 tex 代码的可读性,能够编写以下内容并表示上述内容会很有用。

\kleene{[[some_long_ott_code1]]}
       {[[some_long_ott_code2]]}
       {[[some_long_ott_code3]]}
       {[[some_long_ott_code4]]}.

目前这是不可能的,因为第一个参数后的下一个字符是换行符“字符”,所以下一个参数不会被解析为参数\kleene,而只是一些乳胶(数学)代码。

我怎样才能修改\kleene宏,以便当它检查下一个参数时,不考虑空格(换行符、空格和制表符)?

这样的表述可能需要一个特殊字符(例如,它可以是一个句号)来表明参数列表已经结束。

答案1

您似乎希望\kleene用 触发括号嵌套参数列表中的单个元素的散布\simeq_{k}

我可以提供一个稍微不同的语法,其中括号嵌套/未分隔参数的整个列表将嵌套在一对括号中,以便在该列表上进行迭代,从而利用 (La)TeX 在收集未分隔参数时默默丢弃该参数之前的空格标记这一事实。

这种变体\kleene仅通过宏扩展起作用 — 与基于\futureletand/or\@ifnextchar和/或类似的东西不同。
内部机制不执行任何临时赋值。
结果将在两个扩展步骤后/被\kleene两个“命中”后提供\expandafter
仅通过扩展起作用的情况意味着您可以轻松将的结果\kleene放入另一个宏定义的替换文本中。
无需额外的软件包和 e-TeX 扩展。

\documentclass[a4paper]{article}
\makeatletter
%%----------------------------------------------------------------------
\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 result is delivered after two expansion steps/after having 
%% \UD@ExtractFirstArg "hit" by two \expandafter.
%%.............................................................................
\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}%
  { #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{}.}%
}%
%%------------------------------------------------------------------------------
%% \kleene
%%..............................................................................
\newcommand\kleene[1]{\romannumeral0\kleeneloop{#1}{}{}}%
\newcommand\kleeneloop[3]{%
  % #1 - remaining list of undelimited/brace-nested arguments.
  % #2 - tokens to insert for interspersing.
  % #3 - "interspersed" list constructed so far.
  \UD@CheckWhetherBlank{#1}{ #3}{%
    \expandafter\UD@exchange\expandafter{%
       \expandafter{%
         \romannumeral0\UD@exchange{ }{\expandafter\expandafter\expandafter}%
         \expandafter\UD@exchange\expandafter{%
           \romannumeral0\UD@exchange{ }{\expandafter\expandafter\expandafter}%
           \UD@ExtractFirstArg{#1}%
         }{%
           #3#2%
         }%
       }%
    }{%
      \expandafter\kleeneloop\expandafter{\UD@firstoftwo{}#1}{\simeq_{k}}%
    }%
  }%
}%
\makeatother
\begin{document}

$\kleene{ {a} {b}{c} {d}}$

$\kleene{ a bc d}$

$\kleene{
  {aaaaaaaaa}
  {bbbbbbbbb}
  {ccccccccc}
  {ddddddddd}
  {eeeeeeeee}
}$

% This variant of \kleene works only by means of macro expansion. 
% The internal mechanism doesn't perform whatsoever temporary assignments.
% The result will be delivered after two expansion steps/after having
% \kleene "hit" by two \expandafter. The circumstance that things work 
% only by means of expansion implies that you can easily get the result of
% \kleene into the replacement text of another macro definition:

\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\test
\expandafter\expandafter\expandafter{%
  \kleene{
    {111}
    {222}
    {333}
    {444}
    {555}
    {666}
    {777}
  }%
}%

\texttt{\string\test: \meaning\test}%

$\test$

\end{document}

在此处输入图片描述

答案2

由于您在数学模式下使用它,所以空格会被忽略,因此您可以简化定义,利用\@ifnextchar忽略空格的事实。

\documentclass{article}

\makeatletter
\newcommand\kleene{\@ifnextchar\bgroup{\@kleene}{}}
\newcommand\@kleene[1]{#1\@@kleene}
\newcommand\@@kleene{\@ifnextchar\bgroup{\@@@kleene}{}}
\newcommand\@@@kleene[1]{\simeq_{k}#1\@@kleene}
\makeatother

\begin{document}

$\kleene{a}{b}{c}{d}$

$\kleene{aaaaaaaaa}
        {bbbbbbbbb}
        {ccccccccc}
        {ddddddddd}
        {eeeeeeeee}$

\end{document}

在此处输入图片描述

另一方面,我不会使用可变数量的参数。

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\kleene}{m}
 {
  \seq_set_split:Nnn \l_tmpa_seq { \\ } { #1 }
  \seq_use:Nn \l_tmpa_seq { \simeq\sb{k} }
 }
\ExplSyntaxOff

\begin{document}

$\kleene{a \\ b \\ c \\ d}$

$\kleene{
  aaaaaaaaa \\
  bbbbbbbbb \\
  ccccccccc \\
  ddddddddd \\
  eeeeeeeee
}$

\end{document}

\\使用(或或任何你喜欢的符号)来分隔项目|是更好的语法。 空格(或换行符)\\是可选的。

相关内容