
Quería crear un nuevo comando, que me permita hacer repeat
lo que characters
quiera n times
. Después de buscar e intentar un poco, se me ocurrió esto:
\usepackage{multido}
\newcommand{\myrepeat}[2]{%
\newcount\iterations%
\iterations #1%
\advance\iterations -1
\multido{\iN=0+1}{\iterations}{#2\ }#2%
}
Había un espacio extraño después del comando en el PDF resultante, así que agregué los símbolos de comentario %
y luego desapareció.
Mi pregunta es: ¿Existe una forma mejor de hacer esto, que sea tan fácil de entender como ésta, preferiblemente sin introducir muchas dependencias?
Uno sin él multido
también está bien, si no es demasiado complicado, o si puedes explicarlo para que ya no sea complicado. Supongo que usar multido
para tal cosa está bien, ya que su nombre significa hacer algo varias veces, pero no estoy seguro de haber elegido la forma más fácil y limpia de hacerlo.
Tenga en cuenta que mis soluciones agregan un espacio menos que elementos al pdf. La forma de restar uno me parece sospechosamente detallada.
Tengo dos versiones funcionando ahora:
El 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}{.....}{\ }
}
El 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]{.....}{\ }
}
No sé si uno tiene alguna ventaja sobre el otro. Quizás también haya una mejor manera de eliminar el último espacio en blanco o carácter de separación, en lugar de hacer una iteración menos y escribir solo los caracteres para repetir nuevamente sin el separador. Introduje el separador porque pensé que podría ser útil y es solo un tercer parámetro.
Respuesta1
Sin paquetes:
\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 qué el grupo? Permite llamadas anidadas.
Si solo quieres repetir un personaje:
\documentclass{article}
\newcommand\myrepeat[2]{%
\begingroup
\lccode`m=`#2\relax
\lowercase\expandafter{\romannumeral#1000}%
\endgroup
}
\begin{document}
\myrepeat{4}{x}
\end{document}
Como bien comenta Ulrich Diez, este código no permitiría un registro del contador como número de repeticiones; Para respaldar esto, se ofrece una versión un poco más complicada.
\newcommand\myrepeat[2]{%
\begingroup
\lccode`m=`#2\relax
\lowercase\expandafter{\romannumeral\number\number#1 000}%
\endgroup
}
\romannumeral
desencadena el primero \number
que expande el segundo; si tenemos, digamos, 4 a medida que #1
obtenemos sucesivamente (donde •
denota una ficha de espacio
\romannumeral\number\number4•000
\romannumeral\number4000
\romannumeral4000
mmmm
Si, en cambio, tenemos \count27
as #1
y \count27
mantenemos el valor 4, obtenemos
\romannumeral\number\number\count27•000
\romannumeral\number4000
\romannumeral4000
mmmm
Si tenemos \foo
(un registro contador con nombre), manteniendo nuevamente el valor 4, tenemos
\romannumeral\number\number\foo•000
\romannumeral\number4•000
\romannumeral4000
mmmm
Por lo tantodoscasos de \number
son necesarios para cubrir el tercer caso. Esto aprovecha el hecho de que un espacio finaliza la búsqueda de un número "explícito" y luego se ignora (el token de espacio no se ignora en el tercer caso, después de la expansión del segundo \number
token).
Si también quieres poner un texto entre las repeticiones, el primer método es muy sencillo: simplemente empieza el bucle en 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
(No llames a esto con cero repeticiones).
Una solución diferente con recursividad y la vieja idea de \romannumeral#1000
que produce una larga cadena de m que podemos consumir una a la 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}
La recursividad consumedostokens a la vez, para distinguir si solo le queda un paso más por hacer. El segundo token se devuelve cuando se reinicia la recursividad.
Sin embargo, esto es sólo para estudio. En una aplicación del mundo real lo haría
\usepackage{xparse}
\ExplSyntaxOn
\NewExpandableDocumentCommand{\myrepeat}{O{}mm}
{
\int_compare:nT { #2 > 0 }
{
#3 \prg_replicate:nn { #2 - 1 } { #1#3 }
}
}
\ExplSyntaxOff
ser llamado como \myrepeat[-]{4}{x}
recibir
xxxx
y \myrepeat{3}{A}
para conseguir
aaa
Respuesta2
Aquí hay una versión sin ningún paquete, solo usando simple \loop
, \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}
Respuesta3
FWIW, en ConTeXt puedes usarlo \dorecurse
para repetir algo. Por ejemplo:
\dorecurse{10}{Hello world. }
imprimirá Hello world.
10 veces. El número de iteración actual se almacena en la macro \recurselevel
.
Respuesta4
En OpTeX, puedes usar \fornum
macro:
\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
La \fornum
macro es totalmente ampliable, puede utilizar bucles anidados sin abrir ni cerrar el grupo.