`\begin` ... `\end` 对似乎有问题

`\begin` ... `\end` 对似乎有问题

根据 Leslie Lamport 在其 1985 年出版的《LaTeX 用户指南和参考手册》一书中的说法,宏\begin...\end形成一个组,组内的代码在组外不可见。任何代码重新定义都是该\begin \end组的本地代码。我发现了这个问题。

% epigraphprob.tex SE 558448

\documentclass{report}
\usepackage{epigraph}
\begin{document}
\epigraph{text}{source}  % first epigraph
%...
%{
\begin  % local redefinitions
  \setlength{\epigraphwidth}{0.7\textwidth} % make it wider
  % other changes
  \epigraph{text2}{source2}  % second epigraph
\end   % forget local redefinitions
%}
%...
\epigraph{text3}{source3}  % third epigraph typeset as per first epigraph
%...
\end{document}

上述 MWE 失败,并显示消息“!LaTeX 错误:环境未定义\relax”。

如果我用\begin...替换...那么它就可以起作用。\end{}

有人知道是什么原因造成这种不匹配以及如何解决它吗?


这个问题并不是玩笑,也不是恶搞。兰伯特在书中的第 27 页写道:

范围声明的范围以命令或右括号结束\end。在输入中,括号和\begin命令\end必须成对出现。声明的范围以第一个 匹配的\end或位于声明之前或结束。}\begin{

没有提及接受参数的命令。

我依稀记得有一些简单的命令包含一个声明。Mico 建议\begingroup使用\endgroup\bgroup使用\egroup。这两种组合都使用其中一种,需要的输入比另一种少。Lamport 中没有提到这些。

我老了,记性也差了点,但我应该更仔细地阅读 Lamport,然后看看其他文档,但即使是 LaTeX Companion 也几乎没有提到它们,除了问题和错误消息。GOM。

答案1

和都是宏\begin\end要求一个论点:

\DeclareRobustCommand\begin[1]{%
  \@ifundefined{#1}%
    {\def\reserved@a{\@latex@error{Environment #1 undefined}\@eha}}%
    {\def\reserved@a{\def\@currenvir{#1}%
     \edef\@currenvline{\on@line}%
     \csname #1\endcsname}}%
  \@ignorefalse
  \begingroup\@endpefalse\reserved@a}

为了使其更强大,当前的定义\end稍微复杂一些:

% latex.ltx, line 5654:
\edef\end
  {\unexpanded{%
     \romannumeral
       \ifx\protect\@typeset@protect
       \expandafter       %1
         \expandafter        %2
       \expandafter       %1
           \expandafter         %3 expands the \csname inside \end<space>
       \expandafter       %1
         \expandafter        %2  expands \end<space>
       \expandafter       %1     expands the \else
           \z@
       \else
         \expandafter\z@\expandafter\protect
       \fi
   }%
   \expandafter\noexpand\csname end \endcsname
  }
\@namedef{end }#1{%
  \csname end#1\endcsname\@checkend{#1}%
  \expandafter\endgroup\if@endpe\@doendpe\fi
  \if@ignore\@ignorefalse\ignorespaces\fi}

手册中的描述没有提到参数,但由于两个宏(实际上是名称中带有尾随空格的宏,但这只是实现细节)都需要参数,因此 TeX 将使用其标准规则来查找它。

使用您的代码,的参数\begin\setlength和的参数\end\epigraph。只是为了好玩,\@ifundefined{\setlength}返回 true,所以 TeX 确实

\def\reserved@a{\@latex@error{Environment \setlength undefined}\@eha}

并在执行 后将其展开\begingroup\@endpefalse。由于 的定义\setlength

% latex.ltx, line 2365:
\def\setlength#1#2{#1 #2\relax}

你得到

Environment u n\relax defined

由先前的错误引起的奇怪错误消息。

环境形成一个群,因为 的\begingroup定义中的,与的定义中\begin的 相匹配。但如果论证不恰当,就会出现问题。\endgroup\end

答案2

如果这个问题是合法的。如果有人想使用环境语法来获取临时分配的本地分组,而无需定义任何特定环境,那么最好使用姓名设置作为环境名称,因为这样命令就可以用作环境。对于复杂的文档来说,这比括号更好,因为它显示并强制正确匹配(“你处在一个花括号迷宫中,一切都一样”)。对于问题中的样本,该方法将使用

\begin{setlength}{\epigraphwidth}{0.7\textwidth} % locally make it wider
  % other localized changes
  \epigraph{text2}{source2}  % second epigraph
\end{setlength}   % forget local 

对于来说,它看起来有点笨重\setlength,但对于某些设置来说却很有意义,例如 \begin{bfseries}...\end{bfseries}

有些任务需要在段落末尾激活才能发挥最佳作用,在这种情况下,可以定义“结束”组件来在结束本地组之前结束段落,例如

% for font command \Large used as an environment
\def\endLarge{\par}
...
\begin{Large}
This is an important announcement...
\end{Large}

答案3

(应原帖者的请求,我重新发布了我之前的评论作为答案。)

正如其他答案已经指出的那样,LaTeX\begin\end指令要求一个参数——当然,它们应该是相同的,以便它们能够启动和终止一个有效的 LaTeX环境. 每个重要的 LaTeX 文档都包含最后一个环境——document环境(双关语)。

如果目标不是执行整个环境而“仅仅”定义一个团体-- 在 TeX 特定意义上的“组”一词 --在许多情况下(但并非所有情况下)可以使用和\begingroup或和。(和分别定义为和;当然,这些字符通常(几乎总是?!)被留作 TeX 的分组字符。)顺便说一句,和不是同义词。这很重要,例如,如果一个人处于数学模式。如在\endgroup\bgroup\egroup\bgroup\egroup{}\begingroup\bgroup@egreg 的回答\begin,和的定义分别\end包含\begingroup\endgroup指令;这些指令用于确保 LaTeX 环境的内容形成一个 TeX 组。

\begingroup有关/\endgroup\bgroup/之间差异的更多信息\egroup,请参阅什么时候应使用\begingroup而不是\bgroup

相关内容