取自这个帖子,我有以下代码来定义一个宏,该宏采用任意数量的参数并\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
仅通过宏扩展起作用 — 与基于\futurelet
and/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}
\\
使用(或或任何你喜欢的符号)来分隔项目|
是更好的语法。 空格(或换行符)\\
是可选的。