这个问题与提取宏参数的第一个和最后一个字符?。
看起来,如果除了获取第一个和最后一个令牌之外,还想积累其余的令牌,那么解决方案就不简单了。我的解决方案似乎很复杂。我想知道 David Carlisle 能否立即从口袋里掏出一个更简洁的解决方案。应避免完全展开令牌,因为它们可能包含未定义的控件。
\documentclass{article}
\makeatletter
\usepackage{catoptions}
\def\fl#1{\fl@i#1\@nil\@nil\@nnil}
\def\fl@i#1#2#3\@nnil{%
\def\rest{}\def\last{}%
\edef\first{\ifstrcmpTF{#1}\@nil{}{\unexpanded{#1}}}%
\edef\reserved@b{\unexpanded{#3}}%
\ifcsemptyTF\reserved@b{%
\edef\last{\ifstrcmpTF{#2}\@nil{}{\unexpanded{#2}}}%
}{%
\ifcseqTF\reserved@b\@nnil{%
\edef\last{\ifstrcmpTF{#2}\@nil{}{\unexpanded{#2}}}%
}{%
\edef\last{\unexpanded{#2}}%
\fl@ii#3\@nnil
}%
}%
\ifx\last\rest\def\rest{}\fi
}
\def\fl@ii#1#2#3\@nnil{%
\ifstrcmpTF{#1}\@nil{}{%
\ifstrcmpTF{#2}\@nil{%
\edef\rest{\expandcsonce\last\expandcsonce\rest}%
\edef\last{\unexpanded{#1}}%
}{%
\edef\rest{\expandcsonce\rest\unexpanded{#1}}%
\fl@ii#2#3\@nnil
}%
}%
}
\def\x{\string\x}
\def\y#1{\fl{#1}\immediate\write20{Given token
[\iflacus#1\dolacus empty/null\else#1\fi]
^^J[\first][\last][\rest]}
}
\immediate\write20{[first][last][rest]}
\y{}
\y{\x}
\y{1\x2}
\y{123}
\y{1234\x}
\begin{document}
x
\end{document}
答案1
这是一个解决方案,它所花的时间与标记列表的大小成线性关系,比 egreg 的方法更快。它不保留空格或括号组。
\seq_set_split:Nnn
这个想法是使用一个空的“分隔符”参数(在每个标记之间进行分割)将标记列表转换为一个序列,然后使用函数\seq_pop_left:NN
和\seq_pop_right:NN
从序列中提取(并删除)第一个和最后一个项目。最后,该序列保存标记列表的内容,但不包含其第一个和最后一个元素;我们需要转换回标记列表:这是在\edef
( \tl_set:Nx
) 内完成的,将\unexpanded
( \exp_not:n
) 应用于序列中的每个项目,以防止在此扩展分配中扩展。
\RequirePackage{expl3,xparse}
\ExplSyntaxOn
\seq_new:N \l_your_seq
\DeclareDocumentCommand{\fl}{m}
{
\seq_set_split:Nnn \l_your_seq { } {#1}
\seq_pop_left:NN \l_your_seq \first
\seq_pop_right:NN \l_your_seq \last
\tl_set:Nx \middle { \seq_map_function:NN \l_your_seq \exp_not:n }
}
\ExplSyntaxOff
答案2
LaTeX3 基础设施可用于此:它提供\tl_range:nnn
从令牌列表中抓取令牌的功能。
最后,标记列表变量\l_fl_first_tl
、\l_fl_last_tl
和\l_fl_rest_tl
将包含所需元素。如果标记列表的长度为 1,则其余元素和最后一个元素为空。
\documentclass[a4paper]{article}
\ExplSyntaxOn
\NewDocumentCommand{\fl}{m}
{
\tl_set:Nx \l_fl_first_tl { \tl_range:nnn { #1 } { 1 } { 1 } }
\tl_set:Nx \l_fl_rest_tl { \tl_range:nnn { #1 } { 2 } { -2 } }
\int_compare:nT { \tl_count:n { #1 } > 1 }
{
\tl_set:Nx \l_fl_last_tl { \tl_range:nnn { #1 } { -1 } { -1 } }
}
% now show the result as [first] [last] [rest]
\iow_term:x { === \tl_to_str:n { #1 } === }
\iow_term:x
{
[\tl_to_str:N \l_fl_first_tl]
[\tl_to_str:N \l_fl_last_tl]
[\tl_to_str:N \l_fl_rest_tl]
}
}
\tl_new:N \l_fl_first_tl
\tl_new:N \l_fl_rest_tl
\tl_new:N \l_fl_last_tl
\ExplSyntaxOff
\def\x{aaaa}
\fl{\x}
\fl{1\x2}
\fl{123}
\fl{1234\x}
\fl{1234{\x\x}}
\stop
结果是
===\x ===
[\x ][][]
===1\x 2===
[1][2][\x ]
===123===
[1][3][2]
===1234\x ===
[1][\x ][234]
===1234{\x \x }===
[1][{\x \x }][234]
答案3
这段代码并不假装是最好的解决方案,但它确实是更短。宏一次读取一个标记的输入。非边缘标记被附加到\middle
。处理边缘情况留作练习。然后欢呼\expandafter
!
\documentclass{article}
\makeatletter
\def\split#1{%
\def\middle{}%
\def\last{}%
\let\next=\split@next
\split@#1\@@end}
\def\split@#1{%
\def\first{#1}%
\split@next}
\def\split@next#1{%
\ifx#1\@@end
\let\next=\relax
\else
\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\middle
\expandafter\expandafter\expandafter{\expandafter\middle\last}%
\def\last{#1}%
\fi
\next}
\makeatother
\begin{document}
\split{12345}
\first,\middle,\last
\end{document}
答案4
这是一种使用令牌循环的方法。
\documentclass{article}
\usepackage[T1]{fontenc}
\usepackage{tokcycle}
\def\x{\string\x}
\newcounter{tokcount}
\newcommand\testnext{\stepcounter{tokcount}\tcpeek\zzz}
\def\z#1{\ifx\empty#1\empty[][][]\else
\setcounter{tokcount}{0}\zz#1\endzz\fi}
\def\zz#1#2\endzz{[#1]\tokcycle
{\testnext\tctestifx{\empty\zzz}{[##1]}{\addcytoks{##1}}}
{\testnext\tctestifx{\empty\zzz}{[##1]}{\addcytoks{##1}}}
{\testnext\tctestifx{\empty\zzz}{[##1]}{\addcytoks{##1}}}
{\testnext\tctestifx{\empty\zzz}{[##1]}{\addcytoks{##1}}}
{#2}%
\ifnum\thetokcount=0 []\fi
\expandafter[\the\cytoks]%
}
\begin{document}
[First][Last][Mid]
\z{}
\z{\x}
\z{12}
\z{1\x3}
\z{123}
\z{12 }
\z{1 3}
\z{1234\x}
\z{1234{\x\x}}
\z{1234{\x\x}6} Detokenized Mid: \detokenize\expandafter{\the\cytoks}
\end{document}