Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

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.

share|improve this 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. :-) –  In PSTricks we trust 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. –  In PSTricks we trust 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

3 Answers 3

up vote 12 down vote accepted

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{}.

share|improve this answer
1  
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
1  
@wipet I added \expandafter\empty just for symmetry. Why should be the catcode change redundant? –  egreg Jul 6 at 19:17
1  
@wipet Sorry, no. Using @ commands is recommended for macros the user shouldn't be aware of. –  egreg Jul 6 at 20:08
1  
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
2  
@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]
share|improve this answer

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.

share|improve this answer
    
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
1  
2  
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

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.