
Я хотел создать новую команду, которая позволяет мне делать 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
полностью расширяемый, можно использовать вложенные циклы без открытия или закрытия группы.