我应该把扩展放在哪里以避免在以下代码中出现无限扩展循环?
\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