40

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{...} 
  • 2
    Note that \repeat is already defined by LaTeX as end-macro for \loop. – Martin Scharrer Apr 19 '11 at 19:34
32

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.

  • Ah, I should have known that LaTeX3 is having something like this. I coded something by myself and just saw your post. – Martin Scharrer Apr 19 '11 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. – Will Robertson Apr 20 '11 at 9:20
  • As @Will says, this approach scales well (I use it for testing purposes often with 100k repetitions). – Joseph Wright Apr 20 '11 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 '11 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. – Bruno Le Floch May 13 '11 at 8:41
20

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 …)

  • 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). – Martin Scharrer Apr 19 '11 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 '11 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. – Martin Scharrer Apr 19 '11 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 '11 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 '17 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
    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 '18 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}
  • 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 '11 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 '11 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 '11 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 '11 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 '11 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
  • Yes, but not expandable. Ok, it isn't really necessary in the majority of the cases. – Martin Scharrer Apr 19 '11 at 20:54
  • What does it mean not being expandable? – Christian Lindig Apr 20 '11 at 5:52
  • Christian forgot to notify @Martin I guess, since I don't know the answer to that question. – morbusg Apr 20 '11 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 '11 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 '11 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
  • 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 '16 at 10:20
  • nice but it should be pointed out this doesn't expand fully under \romannumeral-`0. – user4686 Mar 22 '16 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}
  • The request is to repeat a command – Andrew Swann Mar 20 '16 at 15:11
  • Only the simplest commands - e.g. if the new command defines other commands the grouping is not equivalent. – Andrew Swann Mar 20 '16 at 15:23
  • Isn't this the same as the first suggestion in @morbusg's answer? – clemens Mar 20 '16 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
  • 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 '18 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.