LaTeX 中的循环不起作用

LaTeX 中的循环不起作用

我写了这个命令:

\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 .
  1. \loop处理由标记分隔的参数\repeat
  2. \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.

相关内容