扩展中丢失 - 从 1234 变为 1 | 12 | 123 | 1234

扩展中丢失 - 从 1234 变为 1 | 12 | 123 | 1234

我应该把扩展放在哪里以避免在以下代码中出现无限扩展循环?

\documentclass[12pt,a4paper]{article}

\newcommand\split[1]{%
    \splitacc#1\nil
}

\newcommand\accumulator{}

\def\splitacc#1#2\nil{
    \accumulator{}#1%
    \def\old{\accumulator{}#1}          % Expansion(s) missing here ?
    \renewcommand\accumulator{\old{}}   % Expansion(s) missing here ?
    \if\relax\detokenize{#2}\relax\else
        |\splitacc#2\nil
    \fi
}

\begin{document}

\split{1234} % ---> 1 | 12 | 123 | 1234

\end{document}

答案1

如果您不需要 ExplSyntax 和/或您需要可扩展宏:

\def\split#1{\splitA#1\end}
\def\splitA#1#2{#1\ifx\end#2 \else\space | \afterfi \splitA{#1#2}\fi} 
\def\afterfi#1\fi{\fi#1}

\split{1234}

答案2

如果您不需要可扩展的宏:

\documentclass{article}
\usepackage[T1]{fontenc}
\usepackage{xparse}

\ExplSyntaxOn

\NewDocumentCommand{\splitseq}{O{|}m}
 {
  \int_step_inline:nn { \tl_count:n { #2 } }
   {
    \int_compare:nF { ##1 = 1 } { #1 }
    \tl_range:nnn { #2 } { 1 } { ##1 }
   }
 }

\ExplSyntaxOff

\begin{document}

\splitseq{1234}

$\splitseq[\mid]{1234}$

\end{document}

在此处输入图片描述

当然,这可以扩展。请注意,您可以在运行时使用可选参数决定分隔符。

\documentclass{article}
\usepackage[T1]{fontenc}
\usepackage{xparse}
\usepackage{xfp}

\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

\begin{document}

X\splitseq{}X

X\splitseq{1}X

\splitseq{1234}

$\splitseq[\mid]{1234}$

% Just for fun, in order to prove full expandability,
% I use the macro in order to compute 1+11+111+⋯+111111111
\inteval{\splitseq[+]{111111111}}

\end{document}

在此处输入图片描述

答案3

\def\exchange#1#2{#2#1}
\def\split#1{\splitloop{}{}#1\end}
\def\splitloop#1#2#3{%
  % #1 - separator in this iteration
  % #2 - digits accumulated so far
  % #3 - digit or \end collected in this iteration
  \ifx\end#3\else\exchange{#1#2#3\splitloop{ | }{#2#3}}\fi
} 

\tt

(\split{})

(\split{1})

(\split{12})

(\split{123})

(\split{1234})

\bye

在此处输入图片描述


在触发两个扩展步骤后传递结果的变体/在\split被“击中”\expandafter两次后传递结果的变体 - 这在您需要控制扩展/需要知道确切的扩展步骤数直到获得结果的情况下可能会有用 - 要点是:

\romannumeral

  • 在收集属于要用罗马符号表示的数字的数字时触发扩展。
  • 默默地丢弃结束形成该数字的数字序列的空格标记。
  • 在任何情况下都会吞掉构成该数字的令牌序列/数字序列。如果发现数字不为正数,则不会传递任何令牌。

(TeX 的正式介绍⟨数字⟩-TeX 语法的 Backus/Naur 符号的数量可以在 TeXbook 第 24 章:垂直模式摘要中找到。)

因此\romannumeral,只要确保最终\romannumeral收集到的数字不是正数,就可以用来触发大量的扩展和宏观参数交换工作。

如下实现,需要触发一个扩展步骤,以便从以标记 开头的标记序列\split的顶层扩展中获取。 然后需要触发另一个扩展步骤,以获取 的结果。 将首先启动收集标记的过程,这些标记构成要以罗马符号表示的数字。第一个标记是数字“0”,因此收集过程变成了收集更多数字或终止数字序列的标记的过程,因此也终止了收集过程。在收集过程中 - 这是期望的副作用 - 完成了大量扩展和参数交换工作,直到遇到终止收集数字过程的标记。如果该标记是空格标记,它将被默默丢弃。在下面的示例中,“扩展和参数交换工作”集中在形成基于扩展的循环的标记上。 通过调用自身以递归方式根据其宏参数管理事物。当循环终止时,将传递保存结果的宏参数,外加一个前导空格标记。此前导空格标记将被丢弃,并将终止收集数字的过程。因此将找到非正数“0”。由于该数字不是正数,因此将默默地不传递任何罗马数字/任何标记。但您已完成扩展和参数交换工作。\split\romannumeral0
\romannumeral\romannumeral\splitloop\splitloop\romannumeral\romannumeral\romannumeral

\def\firstoftwo#1#2{#1}
\def\secondoftwo#1#2{#2}
\def\split#1{\romannumeral0\splitloop{}{}{}#1\end}
\def\splitloop#1#2#3#4{%
  % #1 - result collected so far
  % #2 - separator in this iteration
  % #3 - digits accumulated so far
  % #4 - digit or \end collected in this iteration
  \ifx\end#4\expandafter\firstoftwo\else\expandafter\secondoftwo\fi
  { #1}{\splitloop{#1#2#3#4}{ | }{#3#4}}%
} 

\tt

\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\macro
\expandafter\expandafter\expandafter{%
\expandafter\expandafter\expandafter(%
  \split{}%
)%
}%
\string\macro: \meaning\macro

\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\macro
\expandafter\expandafter\expandafter{%
\expandafter\expandafter\expandafter(%
  \split{1}%
)%
}%
\string\macro: \meaning\macro

\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\macro
\expandafter\expandafter\expandafter{%
\expandafter\expandafter\expandafter(%
  \split{12}%
)%
}%
\string\macro: \meaning\macro

\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\macro
\expandafter\expandafter\expandafter{%
\expandafter\expandafter\expandafter(%
  \split{123}%
)%
}%
\string\macro: \meaning\macro

\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\macro
\expandafter\expandafter\expandafter{%
\expandafter\expandafter\expandafter(%
  \split{1234}%
)%
}%
\string\macro: \meaning\macro

% Of course shorter would be: 
% \expandafter\def\expandafter\macro\expandafter{\expandafter(\romannumeral0\splitloop{}{}{}1234\end)}
% The point is that the amount of triggers for expansion-steps needed
% for obtaining the result is constant.

\bye

在此处输入图片描述

相关内容