# Without any built-in looping macro, can we just use a recursive macro to get a-b-c-d-e?

Without any built-in looping macro, can we just use a recursive macro to get an output of a-b-c-d-e?

\def\aaa #1{#1-\aaa}
\aaa abcde
% expected result : a-b-c-d-e
\bye


## Edit:

Probably the best way to invoke \aaa should be \aaa{abcde} so the condition to terminate the recursion is still an open question.

-
In plain, \bye is \outer so can't be in the argument to another macro –  Joseph Wright Jul 6 at 14:21
@JosephWright: I finalized the scanning with \relax but it does not help as well. :-) –  cyanide-based food Jul 6 at 14:23
Wasn't quite my point: it's a lot easier to terminate a loop if the token after can simply be grabbed, whereas if it's \outer life is not so straight-forward. What are the rules here for detecting the end of the list for recursion? –  Joseph Wright Jul 6 at 14:27
@JosephWright: \relax might be used to terminate the recursion with \if probably. –  cyanide-based food Jul 6 at 14:31
If you leave an empty line before \bye you'll receive an error too: Runaway argument? and ! Paragraph ended before \aaa was complete. Your recursion is endless. –  egreg Jul 6 at 14:31

If the argument of \aaa is expected to be a string of characters, then

\catcode@=11
\def\aaa#1{\a@a#1\@nil}
\def\a@a#1{\ifx#1\@nil\expandafter\empty\else#1-\expandafter\a@a\fi}
\catcode@=12

\aaa{abcde}

\bye


will do. If the argument is more complicated, with braced groups or macros, it's more difficult.

This is fully expandable, and \edef\foo{\aaa{abcde}}\show\foo would output

> \foo=macro:
->a-b-c-d-e-.


Your first try can't work, because there's no termination condition.

Adding support for braced groups and removing the trailing - can be done as follows:

\catcode@=11
\def\q@stop{\q@stop}
\def\q@nil{\q@nil}
\long\def\@gobble#1{}
\long\def\@firstoftwo#1#2{#1}
\long\def\@secondoftwo#1#2{#2}
\def\aaa#1{\aa@#1\q@stop\q@nil}
\def\aa@#1#2{%
\ifx\q@stop#2%
#1\expandafter\@firstoftwo
\else
#1-\expandafter\@secondoftwo
\fi
{\@gobble}%
{\aa@{#2}}%
}
\catcode@=12

\aaa{abcde}

\aaa{a{bc}de}

\bye


Note that \aa@ has two arguments, so we can absorb a group; the testing is done on the second argument, stopping the recursion if it is \q@stop. I leave as an exercise the case of \aaa{}.

-
The code is curious. First: the \expandafter\empty is redundant. Second: \ifx#1\@nil is equivalent to \ifx#1\undefined because \@nil isn't defined in plain.tex. The catcode changing of @ is redundant too. –  wipet Jul 6 at 18:51
@wipet I added \expandafter\empty just for symmetry. Why should be the catcode change redundant? –  egreg Jul 6 at 19:17
@wipet Sorry, no. Using @ commands is recommended for macros the user shouldn't be aware of. –  egreg Jul 6 at 20:08
Sorry, using @ commands is bad decision. It doesn't prevent to end user to write \def\def or \def\hbox, it brings only complication and the legibility of macros are very reduced. –  wipet Jul 6 at 20:30
@wipet I disagree: the @ per se is not what's making macros illegible. It's the way some people use it: \foo, \f@o, and \f@@ is hard to read while \foo, \foo@auxi, and \foo@auxii is not. And the \def\def thing: not being able to have complete safety doesn't make it bad idea to have at least some safety net IMHO. –  cgnieder Jul 7 at 10:22

The following example uses the space as terminator and supports grouping of letters with braces including empty braces. The solution is expandable:

\def\aaa#1 {%
\aaaX#1\empty\empty
}
\def\aaaX#1#2{%
#1%
\ifx\empty#2\longempty
\expandafter\gobble
\else
-%
\expandafter\aaaX
\fi
{#2}%
}
\long\def\longempty{}
\long\def\gobble#1{}

%%% test %%%

\def\test#1{%
\immediate\write16{[\noexpand\aaa #1 ] => [\aaa #1 ]}%
}
\test{abcde}
\test{a}
\test{}
\test{aaa}
\test{x{}{yy}}
\bye


Result:

[\aaa abcde ] => [a-b-c-d-e]
[\aaa a ] => [a]
[\aaa  ] => []
[\aaa aaa ] => [a-a-a]
[\aaa x{}{yy} ] => [x--yy]

-

The answer by @egreg includes a mistake: the hyphen after the last letter isn't expected by the author of the question. There is full expanable solution which solves it:

\def\aaa #1{\ifx\end#1\end\else \aaaB #1\end\fi}
\def\aaaB #1#2\end{#1\aaaC#2\end}
\def\aaaC #1{\ifx\end#1\empty\else -#1\expandafter\aaaC \fi}

\aaa {abcde}

\bye


Edit: I've added one \ifx in order to accept empty parameter. Grouping of letters with braces including empty braces is supported too.

-
The trailing - was there when I wrote my answer. –  egreg Jul 6 at 20:05
@egreg stackexchange uses a transaction database, all changes are simply traced. But I cannot find the mentioned change in the question. –  wipet Jul 7 at 3:04
–  Stephan Lehmke Jul 7 at 3:40
Why are we trying to prove ourselves to this user for a stupid hyphen ? –  percusse Jul 7 at 20:26
Note that although this style of conditional code looks simpler than the other answers here, having the contents of your recursion macros execute inside the conditionals can cause problems in other contexts. –  Will Robertson Jul 10 at 14:41