Повторите символы n раз

Повторите символы n раз

Я хотел создать новую команду, которая позволяет мне делать repeatвсе, что charactersя хочу для n times. После некоторых поисков и попыток я пришел к следующему:

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

В полученном PDF-файле после команды был какой-то странный пробел, поэтому я добавил символы комментария, %а затем он исчез.

Мой вопрос: есть ли лучший способ сделать это, который был бы таким же простым для понимания, как этот, и желательно не вводил бы много зависимостей?

Без multidoтоже подойдет, если это не слишком сложно, или если вы можете объяснить это так, чтобы это больше не было сложным. Я думаю, что использование multidoдля такой вещи нормально, так как его название означает делать что-то несколько раз, но я не уверен, что выбрал самый простой и чистый способ сделать это.

Обратите внимание, что мои решения добавляют на один пробел меньше, чем добавляют элементов в pdf. Способ вычитания одного кажется мне подозрительно многословным.

У меня сейчас работают две версии:

Измененный вариант от @egreg:

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

Измененный пост от @christian:

\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]{.....}{\ }
}

Я не знаю, есть ли у одного из них какие-либо преимущества перед другим. Может быть, есть и лучший способ удалить последний пробел или разделительный символ, вместо того, чтобы делать на одну итерацию меньше и писать только символы для повторения снова без разделителя. Я ввел разделитель, потому что подумал, что он может быть полезен, и это всего лишь третий параметр.

решение1

Нет пакетов:

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

Почему группа? Она позволяет вложенные вызовы.

введите описание изображения здесь

Если вы просто хотите повторить символ:

\documentclass{article}

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

\begin{document}

\myrepeat{4}{x}

\end{document}

Как справедливо замечает Ульрих Диц, этот код не допускает регистра счетчика в качестве числа повторений; для поддержки этого используется немного более сложная версия

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

\romannumeralзапускает первый \number, который расширяет второй; если у нас есть, скажем, 4, #1мы получаем последовательно (где обозначает пробельный маркер

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

Если вместо этого у нас есть \count27и #1удерживается \count27значение 4, мы получаем

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

Если у нас есть \foo(именованный регистр счетчика), снова содержащий значение 4, то мы имеем

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

Поэтомудваэкземпляры \numberнеобходимы, чтобы охватить третий случай. Это использует тот факт, что пробел завершает поиск «явного» числа и затем игнорируется (маркер пробела не игнорируется в третьем случае, после расширения второго токена \number).

Если вы также хотите вставить текст между повторениями, первый подход очень прост: просто начните цикл с 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

(не вызывайте это с нулевым повторением).

Другое решение с рекурсией и старой идеей \romannumeral#1000создает длинную строку m, которую мы можем использовать по одной за раз.

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

введите описание изображения здесь

Рекурсия потребляетдватокенов за раз, чтобы отличить, остался ли еще один шаг. Второй токен возвращается, когда рекурсия перезапускается.


Однако, это только для изучения. В реальном мире я бы сделал

\usepackage{xparse}

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

называться как \myrepeat[-]{4}{x}получить

хххх

и \myrepeat{3}{A}получить

ААА

решение2

Вот версия без какого-либо пакета, просто с использованием plain \loopи \repeatт. д.

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

введите описание изображения здесь

решение3

Кстати, в ConTeXt можно использовать \dorecurseдля повторения чего-либо. Например:

\dorecurse{10}{Hello world. }

напечатает Hello world.10 раз. Текущий номер итерации сохраняется в макросе \recurselevel.

решение4

В OpTeX можно использовать \fornumмакрос:

\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

Макрос \fornumполностью расширяемый, можно использовать вложенные циклы без открытия или закрытия группы.

Связанный контент