Repeat characters n times

No packages:

\documentclass{article}

\makeatletter
\newcount\my@repeat@count
\newcommand{\myrepeat}[2]{%
  \begingroup
  \my@repeat@count=\z@
  \@whilenum\my@repeat@count<#1\do{#2\advance\my@repeat@count\@ne}%
  \endgroup
}
\makeatother

\begin{document}

\myrepeat{4}{x}

\myrepeat{4}{\myrepeat{2}{x}}

\end{document}

Why the group? It allows nested calls.

enter image description here

If you just want to repeat a character:

\documentclass{article}

\newcommand\myrepeat[2]{%
  \begingroup
  \lccode`m=`#2\relax
  \lowercase\expandafter{\romannumeral#1000}%
  \endgroup
}

\begin{document}

\myrepeat{4}{x}

\end{document}

As Ulrich Diez rightly comments, this code would not allow a counter register as number of repetitions; in order to support this, a slightly more complicated version is

\newcommand\myrepeat[2]{%
  \begingroup
  \lccode`m=`#2\relax
  \lowercase\expandafter{\romannumeral\number\number#1 000}%
  \endgroup
}

\romannumeral triggers the first \number which expands the second one; if we have, say, 4 as #1 we get successively (where denotes a space token

\romannumeral\number\number4•000
\romannumeral\number4000
\romannumeral4000
mmmm

If, instead, we have \count27 as #1 and \count27 holds the value 4, we get

\romannumeral\number\number\count27•000
\romannumeral\number4000
\romannumeral4000
mmmm

If we have \foo (a named counter register), again holding the value 4, we have

\romannumeral\number\number\foo•000
\romannumeral\number4•000
\romannumeral4000
mmmm

Therefore two instances of \number are necessary, in order to cover the third case. This exploits the fact that a space ends the search for an “explicit” number and is then ignored (the space token is not ignored in the third case, after the expansion of the second \number token).

If you also want a text to put in between the repetitions, the first approach is very simple: just start the loop at 1.

\makeatletter
\newcount\my@repeat@count
\newcommand{\myrepeat}[3]{%
  % #1 = number of repetition
  % #2 = text to repeat
  % #3 = text in between
  \begingroup
  #2
  \my@repeat@count=\@ne
  \@whilenum\my@repeat@count<#1\do{#2#3\advance\my@repeat@count\@ne}%
  \endgroup
}
\makeatother

(don't call this with zero repetitions).

A different solution with a recursion and the old idea of \romannumeral#1000 that produces a long string of m's that we can consume one at a time.

\documentclass{article}

\makeatletter
\newcommand\myrepeat[3]{%
  % #1 is the number of repetitions
  % #2 is the code to repeat
  % #3 is the code to put in the middle
  \expandafter\myrepeat@aux\expandafter{\romannumeral\number\number#1 000}{#2}{#3}%
}
\newcommand{\myrepeat@aux}[3]{\myrepeat@auxi{#2}{#3}#1;;}
\def\myrepeat@auxi#1#2#3#4{%
  \ifx#3;%
    \expandafter\@gobble % recursion has ended
  \else
    \expandafter\@firstofone % still one m to swallow
  \fi
  {\myrepeat@auxii{#1}{#2}{#4}}%
}
\def\myrepeat@auxii#1#2#3{%
  #1\ifx#3;\else#2\fi
  \myrepeat@auxi{#1}{#2}#3% restart the recursion
}
\makeatletter

\begin{document}

\myrepeat{4}{x}{-}

\myrepeat{1}{x}{-}

X\myrepeat{0}{x}{-}X

\end{document}

enter image description here

The recursion consumes two tokens at a time, in order to distinguish whether it has only one more step to do. The second token is put back when the recursion restarts.


However, this is just for study. In a real world application I'd do

\usepackage{xparse}

\ExplSyntaxOn
\NewExpandableDocumentCommand{\myrepeat}{O{}mm}
 {
  \int_compare:nT { #2 > 0 }
   {
    #3 \prg_replicate:nn { #2 - 1 } { #1#3 }
   }
 }
\ExplSyntaxOff

to be called like \myrepeat[-]{4}{x} to get

x-x-x-x

and \myrepeat{3}{A} to get

AAA


Here's a version without any package, just using plain \loop and \repeat etc.

\documentclass{article}

\newcount\myloopcounter

\newcommand{\repeatit}[2][10]{%
  \myloopcounter0% initialize the loop counter
  \loop\ifnum\myloopcounter < #1 % Test if the loop counter is < #1
  #2%
  \advance\myloopcounter by 1 % 
  \repeat % start again
}

\begin{document}

\repeatit[5]{A}


\repeatit[20]{And now for something completely different\par}

\end{document}

enter image description here


FWIW, in ConTeXt you can use \dorecurse to repeat something. For example:

\dorecurse{10}{Hello world. }

will print Hello world. 10 times. The current iteration number is stored in the macro \recurselevel.