我想创建一个新命令,它允许我执行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}
正如 Ulrich Diez 正确评论的那样,此代码不允许计数器记录重复次数;为了支持这一点,稍微复杂一点的版本是
\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}
递归消耗二每次处理一个 token,以区分是否只剩下一个步骤需要执行。递归重新启动时,第二个 token 会被放回。
不过,这只是为了学习。在实际应用中,我会这样做
\usepackage{xparse}
\ExplSyntaxOn
\NewExpandableDocumentCommand{\myrepeat}{O{}mm}
{
\int_compare:nT { #2 > 0 }
{
#3 \prg_replicate:nn { #2 - 1 } { #1#3 }
}
}
\ExplSyntaxOff
被称为像\myrepeat[-]{4}{x}
得到
xxxxx
并\myrepeat{3}{A}
得到
AAA
答案2
这是没有任何包的版本,只使用普通的\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
是完全可扩展的,您可以使用嵌套循环而无需打开或关闭组。