![LaTeX 中的循环不起作用](https://linux22.com/image/428645/LaTeX%20%E4%B8%AD%E7%9A%84%E5%BE%AA%E7%8E%AF%E4%B8%8D%E8%B5%B7%E4%BD%9C%E7%94%A8.png)
我写了这个命令:
\newcounter{Lec}
\newcommand{\MakeLecture}[2]{
\setcounter{Lec}{#1}
\loop
\input{\code/Lecture Slides/Chapter0\theLec}
\addtocounter{Lec}{1}\ifnum\value{Lec}<#2 \repeat
}
然后我使用它\begin{document}
来输入多个文件,类似于此代码,
\begin{document}
\MakeLecture{1}{4}
\end{document}
问题在于,文件只输入了第一个文件(Chapter01),而上面的代码应该输入三个文件,Chapter01、Chapter02、Chapter03。那么这个循环有什么问题呢?
答案1
您的代码对我来说只需要一些最少的文件就可以工作,但是\input
循环中间的文件可能会出错,正如 Phelype 在他的评论中指出的那样。
我会使用一种更安全的方法,即在循环中定义一个宏来收集各种信息\input
,并在循环完成时调用此宏。我已更改您的定义,以便插入从 1 到 4 的文件(而不是像您这种情况是 3),因为我觉得这听起来更合理,但如果您不想要,\MakeLecture{1}{4}
您可以删除该行。\advance\count@\m@ne
\documentclass{article}
\makeatletter
\newcommand{\MakeLecture}[2]{%
\count@=#1
\advance\count@\m@ne
\def\@inputmyfiles{}%
\loop\ifnum\count@<#2
\advance\count@\@ne
\edef\@inputmyfiles{\unexpanded\expandafter{\@inputmyfiles}\noexpand\input{Chapter0\the\count@}}%
\repeat
\show\@inputmyfiles % ---> just for testing, delete this in real use
\@inputmyfiles
}
\makeatother
\begin{document}
\MakeLecture{2}{5}
\end{document}
命令\show
返回
> \@inputmyfiles=macro:
->\input {Chapter02}\input {Chapter03}\input {Chapter04}\input {Chapter05}.
答案2
单行代码expl3
(可以嵌套):
\RequirePackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand \MakeLecture { O{1} m }
{ \int_step_inline:nnn {#1} {#2} { \input {Chapter0##1} } }
% { \int_step_inline:nnn {#1} {#2} { \input {\code/Lecture~Slides/Chapter0##1} } }
\ExplSyntaxOff
\MakeLecture{4} % same as \MakeLecture[1]{4}
答案3
由于我没有\input
下面所有示例中的 -files,因此我将\input
-commands 包装到\message
-commands 中,而不是执行它,而是\input
在终端/在调用 latex 进行编译的 shell 窗口中显示相应的 -command。(^^J
表示在终端上开始新行。)
首先我使用你的代码执行此操作:
\documentclass{article}
\newcounter{Lec}
\newcommand{\MakeLecture}[2]{%
\setcounter{Lec}{#1}%
\loop
\message{^^J\string\input{\string\code/Lecture Slides/Chapter0\theLec}^^J}%
\addtocounter{Lec}{1}\ifnum\value{Lec}<#2 \repeat
}
\begin{document}
\MakeLecture{1}{4}
\end{document}
成功了。
使用你的代码(稍加修改的版本),我也得到了此答案底部显示的输出。
因此,对于您的问题“这个循环到底有什么问题?”,我的回答是:
从原则上来说,你的循环似乎没有任何问题。
但这个宏\loop
有一些特殊之处由以下方式揭示\show\loop
——在终端上\show\loop
显示的定义:\loop
\loop=\long macro:
#1\repeat ->\def \iterate {#1\relax \expandafter \iterate \fi }\iterate \let \i
terate \relax .
\loop
处理由标记分隔的参数\repeat
。\loop
\iterate
根据该参数定义一个宏并依赖于该宏\iterate
在循环终止之前不会被重新定义。当循环终止时,\iterate
让其等于\relax
无操作控制序列,该序列在 TeX 的食道中进行宏扩展时不会被移除,但会进入 TeX 的胃中并在那里被移除。
特性 1 意味着:
- 由于分隔符匹配错误,直接嵌套
\loop
...\repeat
不起作用。
特性 2 意味着:
直接嵌套
\loop
...\repeat
可能行不通,因为其中\if...
的不匹配条件\loop..\repeat
可能会导致\if...\fi
定义文本中的错误匹配\iterate
嵌套宏在
\loop..\repeat
某个阶段扩展为\loop
也不起作用:的外部实例
\loop
依赖于\iterate
在循环终止之前不会被重新定义。 的
内部实例在当前范围/组内永久\loop
重新定义。 例如,当内部循环终止时,它们设置为中断外部实例的连续迭代。\iterate
\iterate
\relax
\loop
我猜想这些文件\input
包含对宏的调用,该宏在某个阶段的扩展会产生另一个宏实例,\loop
从而中断您的循环。可能正在使用的某个包在您不知情的情况下带来了这样的宏。
(顺便说一句:\iterate
从用户提供给\loop
-macro 的参数的定义是另一个问题的根源:如果用户提供的参数是定义处理参数的(临时)宏,则这些参数的哈希值(#
)需要加倍,尽管乍一看 -construct\loop..\repeat
你无法看到宏定义内部的宏定义嵌套。)
那么我们能做什么呢?
如果你喜欢奇怪的事情,那么你可以做这样的事情:
\documentclass{article}
\newcounter{Lec}
\csname @ifdefinable\endcsname\AfterMyDelimiter{%
\long\def\AfterMyDelimiter#1#2\MyDelimiter{#2#1\MyDelimiter}%
}%
\newcommand*\MyDelimiter{}%
\newcommand{\MakeLecture}[2]{%
\setcounter{Lec}{#1}%
\loop
\AfterMyDelimiter{%
\message{^^J\string\input{\string\code/Lecture Slides/Chapter0\theLec}^^J}%
\addtocounter{Lec}{1}%
}%
\ifnum#2>\value{Lec}%
\addtocounter{Lec}{1}%
\repeat
\setcounter{Lec}{#1}%
\MyDelimiter
}
\begin{document}
\MakeLecture{1}{3}
\end{document}
尾递归可能更直接一些:
\documentclass{article}
\newcommand{\MakeLecture}[2]{%
\message{^^J\string\input{\string\code/Lecture Slides/Chapter0#1}^^J}%
\csname @\ifnum#1<#2 firstofon\else gobbl\fi e\endcsname
{\expandafter\MakeLecture\expandafter{\number\numexpr#1+1\relax}{#2}}%
}%
\begin{document}
\MakeLecture{1}{3}
\end{document}
如果由于某种原因您希望\input
首先积累呼叫:
\documentclass{article}
\newcommand{\MakeLecture}[2]{\MakeLectureLoop{#1}{#2}{}}%
\newcommand\MakeLectureLoop[3]{%
\csname @\ifnum#1<#2 firstofone\else secondoftwo\fi\endcsname
{\expandafter\MakeLectureLoop\expandafter{\number\numexpr#1+1\relax}{#2}}%
{#3\message{^^J\string\input{\string\code/Lecture Slides/Chapter0#1}^^J}}%
}%
\begin{document}
\MakeLecture{1}{3}
\end{document}
当将上述任一示例保存为test.tex
并进行编译时,我的系统上的 shell 会显示:
$ pdflatex test.tex
This is pdfTeX, Version 3.14159265-2.6-1.40.21 (TeX Live 2020) (preloaded format=pdflatex)
restricted \write18 enabled.
entering extended mode
(./test.tex
LaTeX2e <2020-10-01> patch level 4
L3 programming layer <2021-01-09> xparse <2020-03-03>
(/usr/local/texlive/2020/texmf-dist/tex/latex/base/article.cls
Document Class: article 2020/04/10 v1.4m Standard LaTeX document class
(/usr/local/texlive/2020/texmf-dist/tex/latex/base/size10.clo))
(/usr/local/texlive/2020/texmf-dist/tex/latex/l3backend/l3backend-pdftex.def)
(./test.aux)
\input{\code/Lecture Slides/Chapter01}
\input{\code/Lecture Slides/Chapter02}
\input{\code/Lecture Slides/Chapter03}
(./test.aux) )
No pages of output.
Transcript written on test.log.