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{...}
macrosprogramming
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{...}
You can use \@tfor
. I provide also a better redefinition of the dot under according to your wish:
\documentclass{article}
\usepackage{graphicx}
\let\d\relax
\DeclareRobustCommand{\d}[1]{%
\oalign{#1\cr\hidewidth\scalebox{0.5}{\textbullet}\hidewidth\cr}%
}
\makeatletter
\newcommand{\ds}[1]{%
\@tfor\next:=#1\do{\d{\next}}%
}
\makeatother
\begin{document}
x\d{d}\d{s}\d{a}x
x\ds{dsa}x
\end{document}
What does \@tfor
do? Its syntax is
\@tfor<scratch macro>:=<tokens>\do{<code>}
The scratch macro is traditionally \next
, but it can be anything. The <tokens>
part is any (brace balanced) list of tokens. In the loop, LaTeX essentially does \def<scratch macro>{<next token>}
, so
\@tfor\next:=dsa\do{\d{\next}}
will perform
\def\next{d}\d{\next}\def\next{s}\d{\next}\def\next{a}\d{\next}
However, with \@tfor\next:=d {sa}\do{\d{\next}}
we will just obtain
\def\next{d}\d{\next}\def\next{sa}\d{\next}
Explicit space tokens are ignored and braced groups of tokens are treated as one.
The expl3
analog is \tl_map_inline:nn
:
\documentclass{article}
\usepackage{xparse}
\usepackage{graphicx}
\let\d\relax
\DeclareRobustCommand{\d}[1]{%
\oalign{#1\cr\hidewidth\scalebox{0.5}{\textbullet}\hidewidth\cr}%
}
\ExplSyntaxOn
\NewDocumentCommand{\ds}{m}
{
\tl_map_inline:n { #1 } { \d { ##1 } }
}
\ExplSyntaxOff
\begin{document}
x\d{d}\d{s}\d{a}x
x\ds{dsa}x
x\ds{d sa{bc}}x
\end{document}
No scratch macro is used: the current item in the loop is denoted by #1
(which becomes ##1
in the body of a definition, as usual).
In this particular case where just a single command is applied with the current item as argument, one can use \tl_map_function:nN
:
\NewDocumentCommand{\ds}{m}
{
\tl_map_function:n { #1 } \d
}
which has the same effect and is shorter. It can also appear in a full expansion context (not for this particular case, because of \d
).
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.
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}
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
Best Answer
This can be done in an expandable form using
\csname
. I would personally use the 'pre-packed' version inexpl3
:For those who would code by hand, the basic approach (originally by David Kastrup, modified somewhat by the rest of the team) is
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 exampleThis is clearer to code than the
\csname
approach, but is effectively a loop again and so gets slow for large numbers of repetitions.