2

Where do I put expansions to avoid an infinite expansion loop in the following code ?

\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}
2
  • 1
    If I understand what you are trying to do you need \edef\old{\accumulator{}#1} instead of \def.
    – campa
    Aug 4, 2020 at 11:01
  • Thansk a lot. I have not the LaTeX vocabulary in mind for the moment. Thanks for this.
    – projetmbc
    Aug 4, 2020 at 11:03

3 Answers 3

4

If you don't need ExplSyntax and/or you need expandable macro:

\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}
5

If you don't need an expandable macro:

\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}

enter image description here

Of course this can be made expandable. Note that you can decide the delimiter at run time with an optional argument.

\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}

enter image description here

2
\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

enter image description here


A variant where the result is delivered after triggering two expansion-steps/where the result is delivered, e.g., after \split being "hit" by \expandafter twice - this might be useful in situations where you need to control expansion/where you need to know the exact amount of expansion-steps until obtaining the result - the gist is:

\romannumeral

  • triggers expansion while gathering digits belonging to the number that is to be represented in roman notation.
  • silently discards a space-token ending the digit-sequence which forms that number.
  • in any case swallows the token-sequence/digit-sequence which forms that number. In case the number found not being positive silently no tokens will be delivered at all.

(A formal presentation of TeX's ⟨number⟩-quantities in terms of Backus/Naur-notation of TeX's grammar can be found in the TeXbook, Chapter 24: Summary of Vertical Mode.)

Thus \romannumeral can be used for triggering a lot of expansion- and macro-argument-exchanging-work as long as it is ensured that in the end \romannumeral will have gathered a number which is not positive.

With \split as implemented below one expansion-step needs to be triggered for obtaining from the toplevel-expansion of \split a token sequence which begins with the tokens \romannumeral0.
Then another expansion-step needs to be triggered for obtaining the result of \romannumeral. \romannumeral will first initiate the process of gathering the token(s) which form the number to represent in roman notation. The first token is the digit "0", thus the process of gathering turns into the process of gathering either more digits or a token which terminates the digit-sequence and therefore also terminates the process of gathering. During that process of gathering - this is the desired side-effect - a lot of expansion- and argument-exchanging-work is done until encountering a token which terminates the process of gathering digits. If that token is a space-token, it will silently be discarded. In the example below the "expansion- and argument-exchanging-work" is focused on tokens which form an expansion-based loop \splitloop. \splitloop via calling itself recursively manages things in terms of its macro-arguments. When the loop terminates, the macro-argument which holds the result will be delivered plus a leading space-token. This leading space token will be discarded and will terminate \romannumeral's process of gathering digits. Thus \romannumeral will find the number "0" which is not positive. As that number is not positive, \romannumeral will silently not deliver any roman digits/any tokens at all. But you got the expansion- and argument-exchanging work done.

\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

enter image description here

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .