在下面的 MN(ot)WE 中,我该如何修复\merge
以便它合并由 自动生成的先前值\split
?
最后的代码给出了1 | 12 | 123 | 1234
而不是1 > 12 > 123 > 1234
。
\documentclass[12pt,a4paper]{article}
\newcommand\split[1]{%
\renewcommand\accumulator{}
\splitacc#1\nil
}
\newcommand\accumulator{}
\def\splitacc#1#2\nil{
\accumulator{}#1%
\edef\old{\accumulator{}#1}
\renewcommand\accumulator{\old{}}
\if\relax\detokenize{#2}\relax\else
|\splitacc#2\nil
\fi
}
\newcommand\testbis[1]{%
\merge#1|\nil%
}
\def\merge#1|#2\nil{
#1%
\if\relax\detokenize{#2}\relax\else
$>$ \merge#2\nil%
\fi%
}
\begin{document}
\split{1234}
% --> 1 | 12 | 123 | 1234
\testbis{1 | 12 | 123 | 1234}
% --> 1 > 12 > 123 > 1234
\testbis{\split{1234}}
% --> 1 | 12 | 123 | 1234 instead of 1 > 12 > 123 > 1234
\end{document}
答案1
这是您想要的一个简单实现。
它不太可靠,因为它可能会丢失参数中的括号和空格,而且我没有测试过极端情况:基本上,如果参数由“普通”(非括号和非空格)标记组成,它就可以工作。它应该是有指导意义的,而不是实用的。对于可靠的实现,我会使用expl3
更细粒度的控制来控制不同类型的标记和扩展。
也就是说:您的代码无法工作,因为您的实现\split
不可扩展(它使用赋值),因此您无法拥有类似函数的行为(评估参数,然后评估调用者)。要做到这一点,\split
必须具有可扩展性。
\splitacc
在这种情况下,构建不依赖于赋值的递归相当容易。您可以让它再接受一个参数,即累加器,因此您只需将东西放在那里,它们就会继续对宏进行进一步的调用。我\splitacc{<accum>}<token><token-list>\nil
这样定义,以便它将每个添加到,<token>
直到到达<accum>
末尾<token-list>
。当输入结束时,\splitaccend
循环遍历分离的标记,将它们累积在输出中。
然后,为了让它作为的参数工作,\testbis
我定义了一个\exparg
宏,它(依赖于\expanded
原语和)完全扩展了宏的第一个参数。将其用作。它与的\exparg\macro{<argument>}
相同。expl3
\exp_args:Ne
我还添加了一个宏来修剪项目周围的空格,\testbis
以便间距与实现无关。您可以在分隔符中添加空格。
\documentclass[12pt,a4paper]{article}
\makeatletter
% a quark
\def\qmark{\qmark}
% a macro to trim spaces (not very robust: may lose braces)
\def\trimspace#1{%
\@firstofone{\expandafter\@trimspace
\@firstofone#1 \nil\@trimspace} \nil\@@trimspace\qmark}
\def\@trimspace#1 \nil#2{#2#1\nil\@trimspace}
\def\@@trimspace#1\nil#2\qmark{#1}
% \split starts with an empty accumulator and ends
% with a \qmark to identify the end.
\newcommand\split[1]{%
\splitacc{}#1\qmark\nil}
% \splitacc checks if the end is reached. If so leaves the accumulator,
% otherwise recurses with #1|#2 (this adds a leading | in the first
% iteration which is removed at the end).
\def\splitacc#1#2#3\nil{%
\ifx\qmark#2%
\expandafter\@firstoftwo
\else
\expandafter\@secondoftwo
\fi
{\splitaccend#1\qmark}% use the accumulated string (remove leading marker)
{\splitacc{#1{#2}}#3\nil}}% add to the accumulator and loop
\def\splitaccend#1#2{%
#1%
\ifx\qmark#2
\expandafter\@gobble
\else
|\expandafter\@firstofone
\fi{\splitaccend{#1#2}}}
\newcommand\testbis[1]{%
\merge#1|\nil}
\def\merge#1|#2\nil{%
\trimspace{#1}%
\if\relax\detokenize{#2}\relax\else
$>$\merge#2\nil%
\fi}
% \exparg expands one argument of a macro. Simlar to \exp_args:Ne
\def\exparg#1#2{%
\expandafter#1\expanded{{#2}}}
\begin{document}
\split{1234} % ---> 1 | 12 | 123 | 1234
\testbis{1 | 12 | 123 | 1234}
\exparg\testbis{\split{1234}}
\end{document}
根据要求和expl3
实现,出于教学目的。这里的想法是循环参数标记列表,并根据下一个标记的类型(空格、标记组或“正常”——其他一切)采取不同的操作。入门级宏只是为主循环宏设置环境。此循环宏查看输入中的下一个标记,并根据所述标记的类型选择三个宏中的一个。然后,每个专用宏执行函数的实际工作。
这种条件动作允许您正确处理空格和标记组(取决于您想要的行为)。使用xparse
允许您轻松定义可选参数来更改要查找的标记以及用于替换的标记。
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewExpandableDocumentCommand \split { O{|} m }
{ \mbc_split:Nn #1 {#2} }
% Entry-level function:
\cs_new:Npn \mbc_split:Nn #1 #2
{ \__mbc_split_loop:Nnw #1 { } #2 \q_recursion_tail \q_recursion_stop }
% Looping function to choose type of token:
\cs_new:Npn \__mbc_split_loop:Nnw #1 #2 #3 \q_recursion_stop
{
\tl_if_head_is_N_type:nTF {#3}
{ \__mbc_split_ntype:NnN }
{
\tl_if_head_is_group:nTF {#3}
{ \__mbc_split_group:Nnn }
{ \__mbc_split_space:Nnw }
}
#1 {#2} #3 \q_recursion_stop
}
% Action for 'normal' tokens:
\cs_new:Npn \__mbc_split_ntype:NnN #1 #2 #3
{
\quark_if_recursion_tail_stop:N #3
\tl_if_empty:nTF {#2}
{ \exp_not:n { #3 } }
{ \exp_not:n { #1 #2#3 } }
\__mbc_split_loop:Nnw #1 {#2#3}
}
% Action for grouped tokens:
\cs_new:Npn \__mbc_split_group:Nnn #1 #2 #3
{
\exp_not:n { #1 #2{#3} }
\__mbc_split_loop:Nnw #1 { #2{#3} }
}
\cs_new:Npn \__mbc_split_space:Nnw #1 #2 ~
{
\exp_not:n { #1 #2~ }
\__mbc_split_loop:Nnw #1 { #2~ }
}
%
%
\NewExpandableDocumentCommand \testbis { s D(){|} O{$>$} m }
{
\IfBooleanTF{#1}
{ \mbc_replace:Nne #2 {#3} {#4} }
{ \mbc_replace:Nnn #2 {#3} {#4} }
}
\cs_generate_variant:Nn \mbc_replace:Nnn { Nne }
% Entry-level function:
\cs_new:Npn \mbc_replace:Nnn #1 #2 #3
{ \__mbc_replace_loop:Nnw #1 {#2} #3 \q_recursion_tail \q_recursion_stop }
% Looping function to choose type of token:
\cs_new:Npn \__mbc_replace_loop:Nnw #1 #2 #3 \q_recursion_stop
{
\tl_if_head_is_N_type:nTF {#3}
{ \__mbc_replace_ntype:NnN }
{
\tl_if_head_is_group:nTF {#3}
{ \__mbc_replace_group:Nnn }
{ \__mbc_replace_space:Nnw }
}
#1 {#2} #3 \q_recursion_stop
}
% Action for 'normal' tokens:
\cs_new:Npn \__mbc_replace_ntype:NnN #1 #2 #3
{
\quark_if_recursion_tail_stop:N #3
\token_if_eq_charcode:NNTF #1 #3
{ \exp_not:n {#2} }
{ \exp_not:n {#3} }
\__mbc_replace_loop:Nnw #1 {#2}
}
% Action for grouped tokens:
\cs_new:Npn \__mbc_replace_group:Nnn #1 #2 #3
{ {#3} \__mbc_replace_loop:Nnw #1 {#2} }
% Action for space tokens:
\cs_new:Npn \__mbc_replace_space:Nnw #1 #2 ~
{ ~ \__mbc_replace_loop:Nnw #1 {#2} }
\ExplSyntaxOff
\begin{document}
\split{1234}
\split{1 2{\textit{3}}4}
% * argument forces expansion
\testbis*{\split{1234}}
\testbis*{\split{1 2{\textit{3}}4}}
% ()-delimited argument is the token searched (must be a single token)
% []-delimited argument are the replacement tokens
\testbis(-)[$+$]{1-1 2-1 2{\textit {3}}4}
\end{document}
答案2
这是通过添加Phelype Oleinik 的expl3
宏直接从 egreg 解决方案构建的解决方案。exparg
% Sourceq
% * https://tex.stackexchange.com/a/557325/6880
% * https://tex.stackexchange.com/a/557377/6880
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewExpandableDocumentCommand{\splitseq}{O{|}m}
{
\projetmbc_splitseq:nn { #1 } { #2 }
}
\cs_new:Nn \projetmbc_splitseq:nn
{%
% #1 is the delimiter
% #2 is empty if we don't want the delimiter (first cycle)
% #3 is the accumulated material
% #4 is the next item
% #5 is what remains to be scanned
\__projetmbc_splitseq:nnnw { #1 } { } { } #2 \q_nil \q_stop
}
\cs_new:Npn \__projetmbc_splitseq:nnnw #1 #2 #3 #4 #5 \q_stop
{
\token_if_eq_meaning:NNF #4 \q_nil
{ #2 #3 #4 \__projetmbc_splitseq:nnnw { #1 } { #1 } { #3#4 } #5 \q_stop }
}
\ExplSyntaxOff
\newcommand\testbis[1]{%
\merge#1|\nil}
\def\merge#1|#2\nil{%
#1%
\if\relax\detokenize{#2}\relax\else
$>$\merge#2\nil%
\fi}
\def\exparg#1#2{%
\expandafter#1\expanded{{#2}}}
\begin{document}
\splitseq{1234}
\exparg\testbis{\splitseq{1234}}
\end{document}