根据 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
?