Repetir caracteres n vezes

Repetir caracteres n vezes

Eu queria criar um novo comando, que me permitisse fazer repeatqualquer coisa charactersque eu quisesse n times. Depois de algumas pesquisas e tentativas, cheguei a isto:

\usepackage{multido}
\newcommand{\myrepeat}[2]{%
    \newcount\iterations%
    \iterations #1%
    \advance\iterations -1
    \multido{\iN=0+1}{\iterations}{#2\ }#2%
}

Havia um espaçamento estranho após o comando no PDF resultante, então adicionei os símbolos de comentário %e ele desapareceu.

Minha pergunta é: existe uma maneira melhor de fazer isso, que seja tão fácil de entender quanto esta, de preferência sem introduzir muitas dependências?

Um sem multidotambém está bom, se não for muito complicado, ou se você puder explicar, para que não seja mais complicado. Acho que usar multidopara tal coisa é aceitável, já que seu nome significa fazer algo várias vezes, mas não tenho certeza se escolhi a maneira mais fácil e limpa de fazer isso.

Observe que minhas soluções adicionam um espaço a menos do que adicionam itens ao pdf. A maneira de subtrair um parece-me suspeitamente prolixa.

Eu tenho duas versões funcionando agora:

O de @egreg modificado:

\makeatletter
\newcount\my@repeat@count% initialize a new counter for the loop
\newcommand{\myrepeat}[3]{% new command with 2 arguments
    \begingroup% ???
    \my@repeat@count=1% initialize at 1, so that there are argument - 1 iterations and the last iterations doesn't have a separator following it
    \@whilenum\my@repeat@count<#1\do{#2#3\advance\my@repeat@count1}#2% as long as the iteration count is smaller than the argument, advance, meaning that the counter will be increased by 1
    \endgroup% ???
}
\makeatother

\newcommand{\mediumgap}{%
    \myrepeat{5}{.....}{\ }
}

O de @christian modificado:

\newcount\myloopcounter
\newcommand{\repeatit}[3][10]{%
    \myloopcounter1% initialize the loop counter
    \loop\ifnum\myloopcounter < #1
    #2#3%
    \advance\myloopcounter by 1%
    \repeat% start again
    #2%
}
\newcommand{\longgap}{%
    \repeatit[5]{.....}{\ }
}

Não sei se um tem alguma vantagem sobre o outro. Talvez haja também uma maneira melhor de remover o último espaço em branco ou caractere de separação, em vez de fazer uma iteração a menos e escrever apenas os caracteres para repetir novamente sem o separador. Introduzi o separador porque achei que poderia ser útil e é apenas um terceiro parâmetro.

Responder1

Sem pacotes:

\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}

Por que o grupo? Ele permite chamadas aninhadas.

insira a descrição da imagem aqui

Se você quiser apenas repetir um caractere:

\documentclass{article}

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

\begin{document}

\myrepeat{4}{x}

\end{document}

Como comenta Ulrich Diez, com razão, este código não permitiria um registro contador como número de repetições; para apoiar isso, uma versão um pouco mais complicada é

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

\romannumeralaciona o primeiro \numberque expande o segundo; se tivermos, digamos, 4 à medida que #1obtemos sucessivamente (onde denota um token de espaço

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

Se, em vez disso, tivermos \count27as #1e \count27mantiver o valor 4, obteremos

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

Se tivermos \foo(um registrador contador nomeado), novamente mantendo o valor 4, temos

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

Portantodoisinstâncias de \numbersão necessárias, a fim de cobrir o terceiro caso. Isto explora o fato de que um espaço encerra a busca por um número “explícito” e é então ignorado (o token de espaço não é ignorado no terceiro caso, após a expansão do segundo \numbertoken).

Se você também quiser colocar um texto entre as repetições, a primeira abordagem é muito simples: basta iniciar o loop em 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

(não chame isso com zero repetições).

Uma solução diferente com recursão e a ideia antiga de \romannumeral#1000que produz uma longa sequência de m's que podemos consumir um de cada vez.

\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}

insira a descrição da imagem aqui

A recursão consomedoistokens por vez, a fim de distinguir se há apenas mais uma etapa a ser executada. O segundo token é devolvido quando a recursão é reiniciada.


No entanto, isso é apenas para estudo. Em uma aplicação do mundo real eu faria

\usepackage{xparse}

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

ser chamado gostaria \myrepeat[-]{4}{x}de receber

xxxx

e \myrepeat{3}{A}para conseguir

AAA

Responder2

Aqui está uma versão sem nenhum pacote, apenas usando plain \loope \repeatetc.

\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}

insira a descrição da imagem aqui

Responder3

FWIW, no ConTeXt você pode usar \dorecursepara repetir algo. Por exemplo:

\dorecurse{10}{Hello world. }

imprimirá Hello world.10 vezes. O número da iteração atual é armazenado na macro \recurselevel.

Responder4

No OpTeX, você pode usar \fornummacro:

\def\myrepeat#1#2{\fornum 1..#1\do {#2}}

\myrepeat{4}{x}

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

\message{\myrepeat{4}{\myrepeat{2}{x}}} % prints xxxxxxxx on the terminal.

\bye

A \fornummacro é totalmente expansível, você pode usar loops aninhados sem abrir ou fechar o grupo.

informação relacionada