注意:我看到一个看似重复的问题为什么我不能定义以 \end 开头的命令?提供了一个解决方法,但并没有真正回答问题的“为什么”部分。这个问题旨在真正理解“为什么”部分。
示例代码:
\documentclass{article}
\newcommand{\endnote}{endnote}
\begin{document}
hello
\end{document}
输出:
$ pdflatex foo.tex
This is pdfTeX, Version 3.14159265-2.6-1.40.21 (TeX Live 2020) (preloaded format=pdflatex)
restricted \write18 enabled.
entering extended mode
(./foo.tex
LaTeX2e <2020-10-01> patch level 4
L3 programming layer <2021-02-18>
(/usr/local/texlive/2020basic/texmf-dist/tex/latex/base/article.cls
Document Class: article 2020/04/10 v1.4m Standard LaTeX document class
(/usr/local/texlive/2020basic/texmf-dist/tex/latex/base/size10.clo))
! LaTeX Error: Command \endnote already defined.
Or name \end... illegal, see p.192 of the manual.
See the LaTeX manual or LaTeX Companion for explanation.
Type H <return> for immediate help.
...
l.2 \newcommand{\endnote}{endnote}
?
错误消息要求我阅读手册第 192 页。但我不明白它指的是哪本手册。我想知道 LaTeX 拒绝创建以 开头的命令的确切原因\end
。
答案1
定义时\newenvironment
,会创建两个宏。例如,考虑一个名为 的环境note
。创建的两个宏是\note
和\endnote
。因此,创建的任何环境都会有一个与之关联的以 开头的宏\end...
。新手用户可能没有意识到创建名为 的环境note
会绑定名为 的宏\endnote
。
这个答案可能解释了允许作为宏名的危险\end...
。如果在创建相应环境之后发出,它会破坏环境。如果在创建相应环境之前发出,宏会被覆盖。
因此,我的猜测是明确禁止这样做,以避免这种混淆,因为对于新手来说,名为的宏和名为的环境\newcommand
之间似乎没有任何联系。\endnote
note
\documentclass{article}
\newenvironment{note}{Note:}{The End}
\begin{document}
\begin{note}
Hi mom
\end{note}
\def\endnote{endnote}
\endnote
\begin{note}
Hi mom
\end{note}
\end{document}
答案2
环境通常通过 -macro 来定义\newenvironment
,其中底层宏和定义\⟨environmentname⟩
\end⟨environmentname⟩
。
如果以这种方式定义环境,则其他内容下方的 -macro 将打开一个新的范围/组,并在该范围/组内通过\begin
调用宏。\⟨environmentname⟩
\csname ⟨environmentname⟩\endcsname
如果以这种方式定义环境,则其他事物下的 -macro 将通过\end
调用宏来关闭 -macro 打开的范围/组。\end⟨environmentname⟩
\csname end⟨environmentname⟩\endcsname
\begin
\csname ⟨macroname⟩\endcsname
交付代币\⟨macroname⟩
。如果传递的标记在传递时尚未定义\csname..\endcsname
,则它将被定义为\relax
当前范围/组内的 -primitive。(\relax
-primitive 是不可扩展的无操作,它进入了 TeX 的胃中。)
对于 来说,未定义或定义为\relax
-primitive 的标记是相同的\newcommand
:如果您执行\let\foobar=\relax
,那么您之后就可以完美地执行而不会收到有关已定义的\newcommand{\foobar}...
错误。实际上并不检查要定义的命令是否未定义。实际上检查要定义的命令是否未定义或等于。如果是这种情况,则将执行定义。如果不是这种情况,即,如果要定义的命令的定义不同于,则会引发有关该命令已定义的错误消息。\foobar
\newcommand
\newcommand
\relax
\relax
如果为每个环境定义了一个宏,那么\end⟨environmentname⟩
\newcommand
禁止定义名称中带有短语“\end”的宏就没有必要了。检查已经定义的(底层)宏与和\relax
都足够了。\newcommand
\newenvironment
-macro\newenvironment
是一种定义环境的好工具,但实际上\begin
-macro 和\end
-macro“并不关心”底层宏的定义是如何完成的。\⟨environmentname⟩
\end⟨environmentname⟩
有时环境不是通过使用来定义的\newenvironment
,而是通过直接定义底层宏来定义的,而\⟨environmentname⟩
使-macro 未定义\end⟨environmentname⟩
,依赖于通过-macro 调用会产生-no-op的情况,因为在当前环境的范围/组内宏是未定义的。\csname end⟨environmentname⟩\endcsname
\end
\relax
\end⟨environmentname⟩
\end...
因此,禁止在名称中定义带有的宏可以防止意外/错误地为不应定义的环境定义宏。\end⟨environmentname⟩