由于 egreg 慷慨地同意至少一天不发表评论何时不应使用 \ensuremath 作为数学宏?:-),我想我会利用这一点并发布一个关于 egreg 的另一个(非官方)活动的问题:%
末尾的附加内容\newcommand
和类似的宏。
背景:
在开始使用 LaTeX 并定义大量宏之后,我遇到了 TeX 容量超出问题。经过相当长的时间,我终于想出了一个足够小的例子来重现这个问题,并将其发布在这里:
这段话和下面的内容很好地解释了正在发生的事情:
因此,我养成了%
在每一个行在序言中,甚至\usepackage{}
在 的最后一个}
之后\newcommand
,严格来说它不是必需的。我的逻辑是,我没有看到任何危害,而且添加它比尝试思考是否需要它更容易。
问题:
然而,定义包含分页符和框的命令:
实际上你的 % 中有四个是多余的。...在某些情况下 % 甚至可能是错误的。:) -埃格尔
有时,我甚至会%
在文档正文中添加尾随,但注意到答案被编辑以删除%
。例如,这个答案制作文档的不同版本最初每行后面都有一个结尾%
。现在,在这种情况下,这不是序言的一部分,但我再次认为将它们包括在内并没有什么坏处。
我所知道的唯一一个存在尾随问题的情况%
是在行末百分号(%)有什么用?:
\show\
\show\ %
问题:
我更喜欢不需要太多思考的解决方案,所以我倾向于添加尾随,%
即使它可能不是绝对必要的。所以,我想知道在和类似宏%
的定义中添加尾随什么时候有害\newcommand
?除了上述情况之外还有其他情况吗\show
?
这是 MWE:这两个宏\mymacroA
和\mymacroB
是相同的,除了尾随的%
:
\documentclass{article}
\newcommand\mymacroA[1]{
#1
}
\newcommand\mymacroB[1]{% <-- This percent is important
#1% <-- This percent is important
}% This does not appear to be necessary
\begin{document}
\mymacroA{foo}bar \mymacroA{foo} bar
\mymacroB{foo}bar \mymacroB{foo} bar
\end{document}
答案1
标记化阶段
一般规则∗是控制字 (\par
例如 ) 后的空格将被忽略,而控制符号 (\!
例如 )后的空格将被保留;行首的空格将被完全忽略。连续的空格将转换为一空格标记,但两个连续的行尾变成一个\par
(这个说法并不完全正确,但对于这个答案的目的来说也不是太不正确)。
标记化阶段之后
标记化阶段和后续处理之间存在明显的混合。下文中的“空格”将表示“空格标记”,我不会关心已经消失的空格,例如控制字后面的空格)。
现在,重要的是要知道输入中的空格并不总是会在输出中产生空格。要理解原因,有必要学习一些理论。
TeX 总是处于以下三个状态之一模式:水平、垂直或数学。∗∗
正常情况下空格不会在垂直和数学模式下产生输出(我们不讨论使它们出现的非常特殊的设置)。
很容易判断 TeX 何时进入数学模式:一旦它看到$
或$$
(在 LaTeX 术语中,它们分别是\(
和所有显示数学环境,由、等启动\[
)\begin{equation}
。当它看到关闭的或\begin{align}
时,它会退出数学模式,返回到之前的模式(与之前的 LaTeX 类似)。$
$$
粗略地说,TeX 在作业开始时或在作业开始或之后处于垂直模式,\par
当它开始\vbox
或\vtop
或时;在 LaTeX 中,它们由和\vcenter
开始。但是,垂直和数学模式下的空格是\parbox
\begin{minipage}
不是被抑制:它们存在,但不产生输出。这会导致一些误解。TeX 在看到要排版的字符、\noindent
或\indent
其他一些命令时启动水平模式,特别是\leavevmode
(这不是详尽的列表)或启动(对于LaTeX来说,\hbox
它是\mbox
、、、、... 或环境)。在水平模式下\makebox
\fbox
\colorbox
lrbox
每一个未被其他规则吸收的空间(见下文)会产生输出。
当 TeX 吸收 LaTeX 文档的前言时,它处于垂直模式。但定义如下
\newcommand{\foo}{ bar }
将在输出中产生空格:以上是相等的到
\newcommand{\foo}{ bar }
如果\foo
在段落开头看到,第一个空格不会产生输出(TeX 仍处于垂直模式),但第二个空格会产生输出,因为会b
触发水平模式。相反
\newcommand{\baz}[1]{\parbox{10cm}{
#1
}}
不会显示虚假空格。第一个,before#1
不执行任何操作,因为它是在垂直模式下显示的;最后一个,after#1
被抑制,因为最后一个隐式字符\par
结束了\parbox
。
如下定义
\newcommand{\myop}{ \overset{t }{=} }
不需要%
保护行尾(转换为空格),因为它将在数学模式下使用(否则无论如何都会给出错误)。
应该考虑的最重要(但简单)规则是定义主体中的标记仅被存储,而不被执行\edef
(它们可以在 a或中展开\xdef
,但仍然不能执行)。当定义的宏为用过的在垂直或数学模式下执行空格标记不会产生任何作用,而在水平模式下执行空格标记会在输出中产生空格。
数字和维度
有一个地方,空间标记具有特殊的行为。当 TeX 正在寻找数字或维度以执行分配或扩展\number
和时\romannumeral
,它会扩展标记,直到出现不可扩展的标记或者发现一个空间标记。在这种情况下,空间被作为进程的一部分吞掉。
这是编写宏时要牢记的一个方面。让我们看一个例子:我们想制作一个“每月待办事项清单”。只有一个\parbox
包含十二行、按年份和月份标记的代码;循环似乎是最好的方法,以年份作为参数:
\documentclass{article}
\newcount\monthlycount
\newcommand{\monthlytodo}[1]{\par%
\fbox{%
\parbox{10cm}{%
\monthlycount=1%
\loop\ifnum\monthlycount<13%
#1--\number\monthlycount\hrulefill\par%
\advance\monthlycount by 1%
\repeat%
}%
}%
}
\begin{document}
\monthlytodo{2013}
\end{document}
尝试一下;这会让你大吃一惊
! TeX capacity exceeded, sorry [main memory size=3000000].
哦,天哪!我们已经确保宏没有插入任何虚假空格!为什么 TeX 会背叛我们?很简单:输入可以等效地写为
\loop\ifnum\monthlycount<13#1--\number\monthlycount
并被#1
2013 取代。所以我们的循环检查的当前值是否\monthlycount
小于 132013,并存储标记,\parbox
直到 TeX 内存耗尽。
我们来尝试一下修改后的版本:
\newcommand{\monthlytodo}[1]{\par
\fbox{%
\parbox{10cm}{
\monthlycount=1
\loop\ifnum\monthlycount<13
#1--\number\monthlycount\hrulefill\par
\advance\monthlycount by 1
\repeat
}%
}%
}
瞧!妈妈!没有多余的空格!
练习 1为什么有些行以结尾%
,而其他行没有?我们现在知道这%
是有害之后13
(以及1
前一行的之后)。
技术性:空格标记会被查找,但也会吞掉关键字之后的内容(例如by
在示例和单位名称中mm
,pt
等等)。还有一些其他地方,但这个答案已经太长了。我只会展示在这种情况下使用 LaTeX 函数时可能出现的一些问题:
\newcounter{monthlycount}
\newcommand{\monthlytodo}[1]{\par
\fbox{%
\parbox{10cm}{
\setcounter{monthlycount}{1}
\loop\ifnum\value{monthlycount}<13
#1--\arabic{monthlycount}\hrulefill\par
\stepcounter{monthlycount}
\repeat
}%
}%
}
练习 2%
为什么上面代码的第4-9行没有必要?
脚注
∗一般规则适用于类别代码为 10 的空格(和制表符)。在逐字环境中,或者当空格没有类别代码 10 时,这显然不适用。
∗∗严格来说,这是谎言:确实存在不属于这些模式之一的情况,但这种情况与讨论无关。
答案2
您应该首先了解适用于空格的规则(当然,逐字内部除外):
- 多个空格(包括其他空格(制表符、换行符))将被视为一个空格标记。
两个换行符除外,它们将形成一个\par
。 - 控制字(普通宏)后的空格会被忽略。
这是因为您可能需要添加一个空格来将宏名称与尾随文本分开:例如\relax abc
,因为\relaxabc
这将是不同的控制字。 - 控制字符后的空格(包含单个非字母的宏,如
\_
、\
等,但不是\a
控制字)不会被忽略。
这是因为它们只有一个字符长(反斜杠除外),因此名称的结尾是已知的。 - 行首的空格总是被忽略。
- 直接前言中的空格(包括包和类文件)将被忽略。
这是 LaTeX 特有的功能,只需进行简单配置即可。 - 然而,序言中出现的宏定义内的空格将照常存储进入宏。
这就是为什么需要%
在宏内部使用而不是在宏外部使用(例如在关闭之后)的原因}
。 - 当 TeX 分配计数寄存器或
\ifnum
任何其他数字上下文时,空格会终止 TeX 的数字收集过程。然后该空间将被占用。
因此,如果您写下类似\mycount=123
“您希望\relax
在其后有一个空格”的内容,否则下一个宏将被扩展,因为 TeX 正在搜索数字的潜在其余部分。
请注意,在您的\show
示例中%
, 只是为了保护编辑器,将空格从尾随空格中删除,变成\<space>
。\<newline>
只要空格保留在那里,您就不需要将它放在那里,但它只是保护程序。
然后,确实存在一些高级情况,其中 TeX 允许使用可选空格,您想要添加空格,以便后面的空格不会被视为该可选空格。 我从 中知道这一点\let\dummy=<space>
,它用于从输入流中删除空格。 这样的代码通常用于解析器循环中,删除该空格会导致问题,因为现在删除的是下一个标记,而不是被视为可选空格的空格。 我遇到过这样的情况,只需删除该空格就可以将循环变成无限循环。 有一个答案包含了这个,也许我会再次找到它。
答案3
\newcommand
何时不应在, 或类似符号的行尾添加百分号
当您使用 LaTeX3 的优秀人员提供的便捷环境时,您不需要添加百分号:
\ExplSyntaxOn
... your definitions here
\ExplSyntaxOff
节省打字和百分号!本着http://en.wikipedia.org/wiki/Barometer_question,当问题的标题正如所引用的那样!