根据 | 进行拆分然后合并

根据 | 进行拆分然后合并

在下面的 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}

相关内容