47

Is it possible to define a command, which repeats the following command n-times? Call it for example \Repeat, then

\Repeat[4] \command{...} 

should be equivalent to

\command{...} \command{...} \command{...} \command{...} 
1
  • 2
    Note that \repeat is already defined by LaTeX as end-macro for \loop. Apr 19, 2011 at 19:34

8 Answers 8

35

This can be done in an expandable form using \csname. I would personally use the 'pre-packed' version in expl3:

\documentclass{article}
\usepackage{expl3}
\ExplSyntaxOn
\cs_new_eq:NN \Repeat \prg_replicate:nn
\ExplSyntaxOff
\begin{document}
\Repeat{4}{\command{...}}
\end{document}

For those who would code by hand, the basic approach (originally by David Kastrup, modified somewhat by the rest of the team) is

\catcode `\@ = 11\relax
\long\def\replicate#1{%
  \romannumeral
    \expandafter\replicate@first@aux\number#1%
      \endcsname
}
\long\def\replicate@first@aux#1{%
  \csname replicate@first@#1\replicate@aux
}
\chardef\rm@end=0 %
\long\expandafter\def\csname replicate@first@-\endcsname
  #1{\rm@end\NegativeReplication}
\long\expandafter\def\csname replicate@first@0\endcsname
  #1{\rm@end}
\long\expandafter\def\csname replicate@first@1\endcsname
  #1{\rm@end #1}
\long\expandafter\def\csname replicate@first@2\endcsname
  #1{\rm@end #1#1}
\long\expandafter\def\csname replicate@first@3\endcsname
  #1{\rm@end #1#1#1}
\long\expandafter\def\csname replicate@first@4\endcsname
  #1{\rm@end #1#1#1#1}
\long\expandafter\def\csname replicate@first@5\endcsname
  #1{\rm@end #1#1#1#1#1}
\long\expandafter\def\csname replicate@first@6\endcsname
  #1{\rm@end #1#1#1#1#1#1}
\long\expandafter\def\csname replicate@first@7\endcsname
  #1{\rm@end #1#1#1#1#1#1#1}
\long\expandafter\def\csname replicate@first@8\endcsname
  #1{\rm@end #1#1#1#1#1#1#1#1}
\long\expandafter\def\csname replicate@first@9\endcsname
  #1{\rm@end #1#1#1#1#1#1#1#1#1}
\def\replicate@aux#1{%
  \csname replicate@#1\replicate@aux
}
\long\expandafter\def\csname replicate@\endcsname#1{\endcsname}
\long\expandafter\def\csname replicate@0\endcsname
  #1{\endcsname{#1#1#1#1#1#1#1#1#1#1}}
\long\expandafter\def\csname replicate@1\endcsname
  #1{\endcsname{#1#1#1#1#1#1#1#1#1#1}#1}
\long\expandafter\def\csname replicate@2\endcsname
  #1{\endcsname{#1#1#1#1#1#1#1#1#1#1}#1#1}
\long\expandafter\def\csname replicate@3\endcsname
  #1{\endcsname{#1#1#1#1#1#1#1#1#1#1}#1#1#1}
\long\expandafter\def\csname replicate@4\endcsname
  #1{\endcsname{#1#1#1#1#1#1#1#1#1#1}#1#1#1#1}
\long\expandafter\def\csname replicate@5\endcsname
  #1{\endcsname{#1#1#1#1#1#1#1#1#1#1}#1#1#1#1#1}
\long\expandafter\def\csname replicate@6\endcsname
  #1{\endcsname{#1#1#1#1#1#1#1#1#1#1}#1#1#1#1#1#1}
\long\expandafter\def\csname replicate@7\endcsname
  #1{\endcsname{#1#1#1#1#1#1#1#1#1#1}#1#1#1#1#1#1#1}
\long\expandafter\def\csname replicate@8\endcsname
  #1{\endcsname{#1#1#1#1#1#1#1#1#1#1}#1#1#1#1#1#1#1#1}
\long\expandafter\def\csname replicate@9\endcsname
  #1{\endcsname{#1#1#1#1#1#1#1#1#1#1}#1#1#1#1#1#1#1#1#1}
\catcode `\@ = 12\relax
\edef\test{\replicate{20}{abc}}
\show\test
\bye

In the expl3 version, the \number#1 is (effectively) replaced by \number\numexpr#1\relax, which allows the 'number' used to be a calculation. If you try a negative number, the deliberately-undefined control sequence raises an error as part of the expansion, rather than having some odd error later.


A second expandable approach is to use \romannumeral, for example

\catcode `\@ = 11\relax
\def\replicate#1{%
  \expandafter\replicate@aux\romannumeral\number #1000Q{}
}
\def\replicate@aux#1{\csname replicate@aux@#1\endcsname}
\long\def\replicate@aux@m#1Q#2#3{\replicate@aux#1Q{#2#3}{#3}}
\long\def\replicate@aux@Q#1#2{#1}
\edef\test{\replicate{5}{a}}
\show\test
\bye

This is clearer to code than the \csname approach, but is effectively a loop again and so gets slow for large numbers of repetitions.

6
  • Ah, I should have known that LaTeX3 is having something like this. I coded something by myself and just saw your post. Apr 19, 2011 at 19:56
  • Note that while this seems like a overkill-approach, it should be (much?) more efficient than the simpler looping code proposed in other answers. Apr 20, 2011 at 9:20
  • As @Will says, this approach scales well (I use it for testing purposes often with 100k repetitions).
    – Joseph Wright
    Apr 20, 2011 at 9:28
  • The important point to notice about the code is that it replicates for each power of ten. So as the number gets very big, rather than lots of single repetitions each 'level' requires only one pass.
    – Joseph Wright
    Apr 20, 2011 at 9:56
  • 2
    However, for huge number of repetitions, since all the repetitions are held in TeX's memory, we can overflow it. A solution in that case is \prg_replicate:nn {10000}{\prg_replicate:nn {10000}{...}}. Not needed often. May 13, 2011 at 8:41
21

You can use the \foreach-command from PGF/TikZ.

\documentclass{minimal}
\usepackage{pgffor}
\newcommand{\cmd}{-x-}
% to provide your syntax
\newcommand{\Repeat}[2]{% \repeat already defined
    \foreach \n in {1,...,#1}{#2}
}
\begin{document}
\foreach \n in {1,...,4}{\cmd}

\Repeat{6}{\cmd}
\end{document}

For more information see the pgfmanual.pdf, section 56, pp. 504 an following.

There’s also a TeX-Way see e.g. this page (in german …)

7
  • 4
    pgfs \foreach has the drawback that the command is executed in a group, which might be a big problem or none at all depending on the application. BTW: there is no reason the write {} after \cmd inside the loop, except if you have it as a placeholder for a possible argument (then you should write it as {...} as the OP did). Apr 19, 2011 at 19:33
  • 1
    @MartinScharrer There’s no special reason why I used the {}. You’re right an I edited my post. Could you please outline a situation where the \foreach-group causes problems?
    – Tobi
    Apr 19, 2011 at 20:38
  • 1
    All assignments done in the loop will be executed in their own local group, which could have serious side effects depending on the code. Because you never known what code the user will use it is best to avoid these situations. Apr 19, 2011 at 21:06
  • @MartinScharrer Thank’s. Maybe I remember that in case of getting problem with \foreachif not I’ll be back to ask here ;-)
    – Tobi
    Apr 19, 2011 at 21:57
  • I tried to place multiple tickets on a paper using the ticket package and the \foreach loop. It failed. All tickets are printed at the same location.
    – Lemming
    Jul 27, 2017 at 9:35
12

multido has a simple interface for replication:

enter image description here

\documentclass{article}
\usepackage{multido}
\newcommand{\cmd}{-x-}
\newcommand{\Repeat}{\multido{\i=1+1}}
\begin{document}

\Repeat{6}{\cmd}
\end{document}
1
  • 2
    This is an old answer, but I want just to mention that \i=1+1 is useless here, \newcommand{\Repeat}{\multido{}} is enough.
    – Kpym
    Apr 29, 2018 at 19:47
7

Here some implementation I came up with which doesn't need any extra package. It uses \numexpr to avoid counters and is fully expandable.

\documentclass{article}

\makeatletter
\newcommand{\Repeat}[1]{%
    \expandafter\@Repeat\expandafter{\the\numexpr #1\relax}%
}

\def\@Repeat#1{%
    \ifnum#1>0
        \expandafter\@@Repeat\expandafter{\the\numexpr #1-1\expandafter\relax\expandafter}%
    \else
        \expandafter\@gobble
    \fi
}
\def\@@Repeat#1#2{%
    \@Repeat{#1}{#2}#2%
}
\makeatother

\begin{document}

\Repeat{0}{test }

\Repeat{1}{test }

\Repeat{2}{test }

\Repeat{3}{test }

\Repeat{4}{test }

\Repeat{5}{test }

\edef\TEST{\Repeat{5}{test }}
\texttt{\meaning\TEST}

\end{document}
8
  • If you look at the LaTeX3 implementation, it's expandable even without needing e-TeX. This is some clever code that has been around for many years.
    – Joseph Wright
    Apr 19, 2011 at 20:33
  • @Joseph: I have a really hard time reading the expl3 code, but it looks like it needs e-TeX to me. It uses \int_eval:w a.k.a. \numexpr.
    – TH.
    Apr 20, 2011 at 7:42
  • @TH. The expl3 implementation does indeed need \numexpr, as that makes the nature of the 'number' you give be more flexible. However, the original version of this approach just uses \number, and thus no e-TeX. Later on today I'll post the code 'translated' to plain TeX as a separate answer.
    – Joseph Wright
    Apr 20, 2011 at 8:09
  • @TH. I've added the plain TeX code to my answer, avoiding e-TeX but explaining why it is used for the expl3 version.
    – Joseph Wright
    Apr 20, 2011 at 8:34
  • @Joseph: I see now, that's very clever! Thanks. (One of these days, I really just need to learn expl3.)
    – TH.
    Apr 20, 2011 at 8:49
6

Plain and simple (pun intended):

\def\foo{keke}
\def\bar#1#2{\count0=#1 \loop \ifnum\count0>0 \advance\count0 by -1 #2\repeat}
\bar3\foo % results in kekekekekeke
\bye

Token list registers expand more quickly, so if it suits you, you could also do:

\newtoks\foo \foo={keke}
\def\bar#1#2{\count0=#1 \loop \ifnum\count0>0 \advance\count0 by -1 \the#2\repeat}
\bar3\foo % results in kekekekekeke
\bye

Repeating in an expendable way using e-tex additions (from http://www.tug.org/TUGboat/tb29-2/tb92jackowski.pdf):

\def\foo{keke}
\def\gobbleone#1{}
\long\def\replicate#1#2{% 
  \ifnum\numexpr#1>0 
    #2\expandafter\replicate\expandafter 
    {\number\numexpr#1-1\expandafter}% 
  \else 
    \expandafter\gobbleone 
  \fi{#2}}
\replicate3\foo % results in kekekekekeke
\bye
6
  • Yes, but not expandable. Ok, it isn't really necessary in the majority of the cases. Apr 19, 2011 at 20:54
  • What does it mean not being expandable? Apr 20, 2011 at 5:52
  • Christian forgot to notify @Martin I guess, since I don't know the answer to that question.
    – morbusg
    Apr 20, 2011 at 7:06
  • 1
    @Christian: Something is expandable if you can use it inside an \edef or \write an TeX converts it fully to what you want. Assignments are not expandable, and so a loop using a count will not turn into a series of repeated commands inside an \edef.
    – Joseph Wright
    Apr 20, 2011 at 7:06
  • @Christian: I thought that the general question had been asked, but cannot find it. You might wish to ask about expandability in general, and those of us who understand it can try to explain!
    – Joseph Wright
    Apr 20, 2011 at 7:08
4

Of course, the simple answer to the OP is: use \loop. But there are more codes here signed as "clever", if the loop is done only at expand processor level. And more "clever" codes here do this without using of eTeX primitives.

So, I give two codes here. You can explore them from "cleverness" point of view:).

First code uses known trick with \romannumeral #1000 which expands to #1 "em"s and then we do loop over these "em"s over input stream. The main difference from the similar code presented here by @Joseph is that we needn't to read whole long sequence of "em"s again and again:

\def\replicate#1{\expandafter\repU\expandafter{\romannumeral\number#1000}}
\def\repU#1#2{\repV{#2}#1;}
\def\repV#1#2{\ifx#2;\else#1\fihere\repV{#1}\fi}
\def\fihere#1\fi{\fi#1}

\message{\replicate{27}{abc}}
\bye

The second code is inspired by second code presented here by @Joseph where each decimal digit is processed individually. The code (in the accepted answer) uses nested \csnames. I do the same without nested \csnames and the code is more compact. Of course, it works at expand processor level without any need of eTeX primitives:

\def\replicate#1{\expandafter\repA\number#1;;}
\def\repA#1#2;#3;{\ifx;#2;\repB#1#3\fi \repA#2;#1#3;}
\def\repB#1\fi#2;#3;{\fi\repC{#1}}
\def\repC#1#2{\repD{#2}#1;}
\def\repD#1#2{\ifx#2;\else \repE{#1}#2\fi}
\def\repE#1#2\fi{\fi \repF#2{#1}\repD{\repF{10}{#1}}}
\def\repF#1#2{\ifcase#1 \or#2\or#2#2\or#2#2#2\or#2#2#2#2\or#2#2#2#2#2\or
   #2#2#2#2#2#2\or#2#2#2#2#2#2#2\or#2#2#2#2#2#2#2#2\or
   #2#2#2#2#2#2#2#2#2\or#2#2#2#2#2#2#2#2#2#2\fi}

\message{\replicate{27}{abc}}
\bye
2
  • My comment about eTeX was probably misleading: I've removed it (you need eTeX if you want to accept integer expressions for the number of repeats, not for the core idea of making copies).
    – Joseph Wright
    Mar 21, 2016 at 10:20
  • nice but it should be pointed out this doesn't expand fully under \romannumeral-`0.
    – user4686
    Mar 22, 2016 at 22:24
2

Here is an example with loop and newcommand:

\documentclass{article}
\newcounter{z}
\newcommand\y[2]{
  \loop \ifnum\value{z} < #1
    #2%
    \stepcounter{z}%
  \repeat
}
\begin{document}
\y{10}{Hello}
\end{document}
3
  • The request is to repeat a command Mar 20, 2016 at 15:11
  • Only the simplest commands - e.g. if the new command defines other commands the grouping is not equivalent. Mar 20, 2016 at 15:23
  • Isn't this the same as the first suggestion in @morbusg's answer?
    – cgnieder
    Mar 20, 2016 at 15:27
2

I tried an alternative to the clever \csname governed expansion from Joseph's answer borrowed from expl3 code.

eTeX is used only to allow input to be an expression: else replace \the\numexpr at the start by \number.

This is less efficient than the David Kastrup + LaTeX team code, although perhaps it becomes about the same when the number of replications is in the thousands (not much tested).

The initial version of this answer had more complicated code which was at about the same level of efficiency. There was an unfortunate \chardef\z@=0 in that code, which is very wrong and I don't know why it was there.

This answer handles more efficiently than Joseph's the case of a negative asked for number of replication.

It could be easily reworked into a macro (working only in a \edef) leaving tokens behind it rather than in front of it while expanding.

\catcode`@ 11

\def\JFsignfork #10-#2#3\krof {#2}
%\chardef\z@ 0 % NO! \z@ is a dimen in TeX/LaTeX

\def\JFrep   #1{\romannumeral\expandafter\JFrep@a\the\numexpr #1;3456789XY!}%
\def\JFrep@a #1{\JFsignfork
                  #1-\JFrep@nil
                  0#1\JFrep@neg
                   0-\JFrep@b
              \krof #1%
             }%
\long\def\JFrep@nil #1!#2{\z@}
\long\def\JFrep@neg #1!#2{\z@\NegativeReplication}

% TeX numbers have at most 10 digits
\def\JFrep@b #1#2#3#4#5#6#7#8#9{\JFrep@c {.#9.#8.#7.#6.#5.#4.#3.#2;#1}}
\def\JFrep@c #1#2#3#4!{\JFrep@d .#3.#2#1!}
\def\JFrep@d #1;#2#3{\csname JFrep@f#2#3\endcsname}
\long\expandafter\def\csname JFrep@f.0\endcsname #1#2#3!#4%
     {\csname JFrep@f#1#2\endcsname#3!{#4#4#4#4#4#4#4#4#4#4}}%
\long\expandafter\def\csname JFrep@f.1\endcsname #1#2#3!#4%
     {\csname JFrep@f#1#2\endcsname#3!{#4#4#4#4#4#4#4#4#4#4}#4}%
\long\expandafter\def\csname JFrep@f.2\endcsname #1#2#3!#4%
     {\csname JFrep@f#1#2\endcsname#3!{#4#4#4#4#4#4#4#4#4#4}#4#4}%
\long\expandafter\def\csname JFrep@f.3\endcsname #1#2#3!#4%
     {\csname JFrep@f#1#2\endcsname#3!{#4#4#4#4#4#4#4#4#4#4}#4#4#4}%
\long\expandafter\def\csname JFrep@f.4\endcsname #1#2#3!#4%
     {\csname JFrep@f#1#2\endcsname#3!{#4#4#4#4#4#4#4#4#4#4}#4#4#4#4}%
\long\expandafter\def\csname JFrep@f.5\endcsname #1#2#3!#4%
     {\csname JFrep@f#1#2\endcsname#3!{#4#4#4#4#4#4#4#4#4#4}#4#4#4#4#4}%
\long\expandafter\def\csname JFrep@f.6\endcsname #1#2#3!#4%
     {\csname JFrep@f#1#2\endcsname#3!{#4#4#4#4#4#4#4#4#4#4}#4#4#4#4#4#4}%
\long\expandafter\def\csname JFrep@f.7\endcsname #1#2#3!#4%
     {\csname JFrep@f#1#2\endcsname#3!{#4#4#4#4#4#4#4#4#4#4}#4#4#4#4#4#4#4}%
\long\expandafter\def\csname JFrep@f.8\endcsname #1#2#3!#4%
     {\csname JFrep@f#1#2\endcsname#3!{#4#4#4#4#4#4#4#4#4#4}#4#4#4#4#4#4#4#4}%
\long\expandafter\def\csname JFrep@f.9\endcsname #1#2#3!#4%
     {\csname JFrep@f#1#2\endcsname#3!{#4#4#4#4#4#4#4#4#4#4}#4#4#4#4#4#4#4#4#4}%
\long\expandafter\def\csname JFrep@f;1\endcsname!#1{\z@#1}%
\long\expandafter\def\csname JFrep@f;2\endcsname!#1{\z@#1#1}%
\long\expandafter\def\csname JFrep@f;3\endcsname!#1{\z@#1#1#1}%
\long\expandafter\def\csname JFrep@f;4\endcsname!#1{\z@#1#1#1#1}%
\long\expandafter\def\csname JFrep@f;5\endcsname!#1{\z@#1#1#1#1#1}%
\long\expandafter\def\csname JFrep@f;6\endcsname!#1{\z@#1#1#1#1#1#1}%
\long\expandafter\def\csname JFrep@f;7\endcsname!#1{\z@#1#1#1#1#1#1#1}%
\long\expandafter\def\csname JFrep@f;8\endcsname!#1{\z@#1#1#1#1#1#1#1#1}%
\long\expandafter\def\csname JFrep@f;9\endcsname!#1{\z@#1#1#1#1#1#1#1#1#1}%

\catcode`@ 12

\edef\test{\JFrep{123}{abc}}
\show\test

\bye
1
  • There is now an \xintreplicate in xint but it is a clone of the expl3's code with very minor changes, it is not the code of this answer...
    – user4686
    Apr 29, 2018 at 20:26

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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