有些人交替使用“宏”和“命令”这两个词来指代给 LaTeX 的指令。这两个术语之间有真正的区别吗?如果有,我们能否看一些例子来说明它们的区别?
答案1
当涉及 (La)TeX 时,我很少使用“命令”这个术语。
在 TeXbook 中,“命令”一词也出现在不是单个标记而是控制字标记序列的出现发挥作用的上下文中。
例如,TeXbook 的第 15 章包含一个双重危险弯道段落,您可以在其中阅读:
[...] 因此,纯 TeX 提供了寄存器的分配函数;附录 B 包括
\newinsert\footins
定义\footins
为脚注插入编号的命令。[...]
例如,\newinsert\footins
是一个“命令”。
(顺便说一句:提到的附录 B 实际上并没有展示标记。它展示了 TeX 代码,即纯 TeX 格式的程序员生成的 TeX 输入。该 TeX 输入本身不是标记。TeX 在处理该代码时会创建标记。)
在 TeXbook 中,您还可以找到短语“原始命令”。它用于非常短且易于掌握的 TeX 输入序列发挥作用的上下文中。(读取和标记时产生原始序列的序列...)
我想,“命令”根据 TeXbook 的措辞,它是构成 TeX 输入一部分的字符序列。TeX 输入的一部分,当 TeX 读取并标记该字符序列时,会产生一组标记和/或(例如,如果相关命令包含“无效字符”/类别代码为 15 的字符)一些错误消息。如果产生一组标记,则该组标记旨在形成一个语法实体,TeX 的标记处理设备可以尝试以任何方式执行/处理该语法实体。这种尝试反过来也可能产生错误消息和/或错误行为。
因此,TeXbook 本身的措辞中的“命令”一词似乎实际上并没有将事物作为 TeX 的“消化过程”的结果。TeX 消化过程的早期结果之一是代币。代币是在 TeX 的嘴里产生的。因此“命令”一词本身似乎实际上并不是指代币。它似乎是指 TeX 用“眼睛”所“观察”的事物的子集 — — 它指的是 TeX 输入的部分,即用户/程序员在交互模式下可以输入的内容,或者 TeX 可以从 .tex 输入文件或\read
句柄中读取以进行标记和进一步处理的内容。
在 LaTeX 2e-kernel 中,您有类似\newcommand
和 的东西\renewcommand
。
这些事情在 LaTeX 的消化过程中导致 LaTeX 执行任务。但观点似乎不是 LaTeX 程序的观点,它“查看”LaTeX 输入,同时“消化”产生“标记”等。观点似乎是程序员的观点。程序员不产生标记。她/他产生 LaTeX 输入。该 LaTeX 输入还不是标记。它是输入字符的序列。
编写完成后\newcommand...
,程序员可以在其代码中将通过引入的内容作为\newcommand
LaTeX 输入的一小部分,当通过 LaTeX 标记时,会产生一个标记,该标记本身被视为句法实体,当按预期使用时,可以通过 LaTeX 的标记处理设备进行,而不会产生错误行为。
我在 texbook.tex 中找不到短语“命令标记”。
这也可以理解为 TeXbook 词汇中的“command”和“token”最终不是指同一阶段的工作。
看来,在 TeXbook 中,当重点放在程序员生成/传递/输入任何 .tex 输入的阶段时,使用术语“命令”。
看来,在 TeXbook 中,当重点关注 TeX 程序处理 .tex 输入的阶段时,使用术语“标记”和“宏”。
术语“宏”似乎是指 TeX 标记的概念,而标记和宏标记并不是在编写/生成/传递 TeX 输入时产生的,而是在 TeX 程序读取和标记 TeX 输入并从生成的标记中扩展可扩展标记时产生的。
据我所知,宏代币是⟨控制序列⟩两者都是可扩展的,并且不具有被称为“原语”的约 300 个低级原子操作之一的含义。
除了“宏”和“命令”之外,TeX 世界中还有另一个术语值得尝试阐明:术语“函数”。
术语“函数”在 expl3 的文档中被大量使用——expl3 是 LaTeX 的一个流行且最先进的编程层。
宏标记在 TeX 的管道中处理,其中可扩展标记的扩展以某种“反刍过程”进行。宏标记在单个扩展步骤中在 TeX 的管道中进行处理。扩展步骤之后,宏标记和形成其参数和参数分隔符的标记将从标记流中移除,而根据宏定义形成替换文本的标记将插入到标记流中。扩展步骤之后,宏的执行就完成了。
执行宏并不一定意味着完全执行算法。执行宏意味着执行例程/算法的单个扩展驱动步骤。
“功能”一词侧重于完全执行例程/算法以获得所需结果的结果扩展步骤和其他类型的操作步骤的级联——即调用(几个)宏和/或调用原语(无论是否可扩展),和/或调用不可扩展的非原语——可能发挥作用。
实现“功能”可能需要定义构成“机制”的几个宏和不可扩展的非原语。
用expl3-jargon该标记/控制序列也称为“函数”,它启动扩展步骤的级联和/或任何其他操作步骤,从而执行相关的算法/例程。
上面我说了:
“据我所知,宏代币⟨控制序列⟩两者都是可扩展的,并且不具备被称为‘原语’的约 300 个低级原子操作之一的含义。”
什么类型的代币可以形成这样的⟨控制序列⟩?
⟨控制序列⟩有两种类型:
- ⟨控制序列 代币⟩
这些分为⟨控制符号 代币⟩和⟨控制字 代币⟩ - ⟨主动角色 代币⟩
这句话触及了如何分配的问题⟨控制序列⟩。
正如刚才看到的,一种划分方式⟨控制序列⟩是通过研究什么类型的 token 可以形成⟨控制序列⟩:
⟨控制序列 代币⟩是那些可爱的“小玩意”,在 TeX 输入中,它们前面需要有一个类别代码为 0 的字符(转义)。
通常,反斜杠字符是唯一一个类别代码为 0 的字符。
⟨控制序列 代币⟩有两种类型:
⟨控制符号标记⟩名称由不包含类别代码 11(字母)的单个字符组成。
例如,通常\.
和\\
通常⟨控制符号标记⟩与通常的字符一样.
,\
没有类别代码 11(字母)。 \a
通常不是⟨控制符號⟩因为通常字符a
确实有类别代码 11(字母)。
⟨控制词标记⟩其名称由多个不需要具有相同类别代码的字符组成,或者由具有类别代码 11(字母)的单个字符组成。
有两种方法可以⟨控制序列⟩(即⟨控制序列标记⟩或 ⟨活跃角色标记⟩(见下文)可以产生:
- 当 (La)TeX 读取并标记输入时。
- 当 (La)TeX 在扩展过程中插入它时;例如,作为⟨替换文本⟩宏;例如,作为
\the
标记寄存器扩展的一部分。
A⟨控制序列标记⟩也可以实现
- 作为扩展某些表达式的结果
\csname..\endcsname
。
任何当前类别代码不是 11(字母)的字符,即使是类别代码为 15(无效)的字符,都可以形成⟨控制符號⟩这是通过读取和标记 TeX 输入而产生的。
(我说“当前”,是因为一旦构成 TeX 名称的字符⟨控制序列标记⟩问题中的字母被切换到11,⟨控制序列标记⟩不会被视为⟨控制符號⟩(不再)但作为⟨控制字标记⟩。
否则,你可以不使用⟨控制符号标记⟩用于更改当前类别代码为 15 的字符的类别代码(无效)。
但你可以这样做,例如:
\catcode`\A=15 % Now uppercase-a is invalid.
% Typing an "A" both outside a comment and not behind a character
% of category code 5 (end of line) which is not
% intended to form the name of a control-symbol-token would yield
% an error-message now.
% But you can do:
\catcode`\A=11 % Now uppercase-a is a letter again.
在详细说明了⟨控制符号标记⟩,让我们详细了解一下⟨控制词标记⟩:
一方面,TeX 的读取和标记装置只会将类别代码为 11(字母)的字符序列作为⟨控制词标记⟩.
因此名称⟨控制词标记⟩通过让 TeX 直接从 TeX 输入读取并标记它们而创建的,仅包含在读取和标记它们时具有类别代码 11(字母)的字符。
另一方面,⟨控制词标记⟩通过评估\csname..\endcsname
-expressions 创建的可以是由在读取和标记 -expression 时不同于 5、9、14 和 15 的任意类别代码的一个以上的字符组成\csname..\endcsname
,或者是由类别代码 11(字母)的单个字符组成。
现在来谈谈 TeX 处理⟨控制符号标记⟩以及 TeX 的处理⟨控制词标记⟩:
通常,空格字符(ASCII 和 unicode 中的代码点 32)和水平制表符(ASCII 和 unicode 中的代码点 9)是唯一具有类别代码 10(空格)的字符。
在 TeX 输入中,类别代码为 10(空格)的字符出现在被标记为⟨控制符號⟩其名称由不具有类别代码 10(空格)的字符组成,将被标记为显式空格标记。
在 TeX 输入中,类别代码为 10(空格)的字符出现在被标记为⟨控制符號⟩其名称由类别代码为 10(空格)的字符组成,将被忽略/不会产生任何标记。
例如,类别代码 10(空格)- 控制空格后面的字符,\␣
即⟨控制符號⟩其名称由空格字符组成,将被忽略/不会产生任何标记。
在 TeX 输入中,类别代码为 10 的字符(空格)出现在被标记为的字符序列后面⟨控制字标记⟩将被忽略/不会产生任何令牌。
当 (La)TeX 执行未扩展的写入操作时⟨控制字标记⟩(无论是写入文件还是写入屏幕),它都会自动在表示字符序列的末尾插入一个空格字符⟨控制字标记⟩有问题。
当 (La)TeX 执行未扩展的写入操作时⟨控制符號⟩它不会自动插入这样的尾随空格字符。
当申请\string
将以下 token 转换为⟨字符标记⟩(La)TeX 不会附加额外的尾随空格标记。
(\string
产生第 12 类(其他)字符标记。例外:由 产生的空格\string
将被称为显式空间标记,即字符代码为 32 且属于类别 10(空格)的字符标记。)
你可以改变 TeX 执行未展开写入的方式⟨控制序列标记⟩其名称由单个字符组成,在书写之前将该字符的类别代码更改为 11(字母)——在这种情况下,它将被写为⟨控制字标记⟩带有尾随空格(或设置为不同于 11 的值),在这种情况下,它将被写为⟨控制符號⟩没有尾随空格。
因此,你可以在使用未扩展的 TeX 进行延迟写入时自动插入空格。⟨控制序列标记⟩其名称由一个字符组成——代码
\documentclass{article}
\begin{document}
\newwrite\mywrite
\immediate\openout\mywrite test.txt\relax
\catcode`\A=12
\write\mywrite{b\noexpand\Ab}
\immediate\write\mywrite{b\noexpand\Ab}
\catcode`\A=11
Hello
\end{document}
此外,.dvi 文件或.pdf 文件还会产生一个包含以下内容的文件 test.txt:
b\Ab
b\A b
由于两个\write
命令都是在类别代码为 12(其他)时发出的A
,因此您可能希望每个命令\A
都未展开,写为⟨控制符號⟩没有尾随空格,因此你可能期望输出
b\Ab
b\Ab
。但第一个\write
不是\immediate
,因此它不是立即执行的,而是在页面发送时执行的。当时,第二个\write
,即\immediate
,已经完成,并且\A
再次具有类别代码 11(字母),因此\A
第一个中的\write
被写在第二行中作为⟨控制字标记⟩,LaTeX 会附加一个尾随空格字符。
无名氏⟨控制序列标记⟩— 形成它的方法包括:1)扩展\csname\endcsname
;2)在行尾放置反斜杠(类别代码 0 的字符(转义)),同时参数\endlinechar
具有非正值或超出 TeX 引擎输入编码的代码点范围的值 — 值得特别注意:
应用于\string
它通常会产生类别代码 12(其他)字符标记序列。和 之间没有空格标记。也没有尾随空格标记。 未扩展的书写会产生字符序列— 有一个尾随空格字符。 TeX 在向外部文本文件或屏幕进行非扩展写入时,不一定会使用/backslash-characters 产生反斜杠字符标记,但会生成那些在 TeX 引擎的内部字符表示方案中的字符代码/代码点编号(在传统 TeX 中是 ASCII,在 LuaTeX/XeTeX 中是 unicode,其中 ASCII 是其严格子集)等于整数参数 的值的字符标记/字符。 如果 的值超出了字符编码可能的代码点编号范围,则 TeX 将不会提供转义字符标记/字符。 通常 的值为92,表示反斜杠。但在命令之后会屈服,因为 65 是字符 的代码点编号。 但在命令之后会屈服,因为 -8 超出了字符编码可能的代码点编号范围,因此不会提供表示转义字符(反斜杠)的标记。 通过 产生的字符标记属于类别 12(其他)。唯一的例外是:如果通过 产生的字符标记的字符代码是 32(32 表示空格字符),则类别为 10(空格)。\12c12s12n12a12m12e12\12e12n12d12c12s12n12a12m12e12
\12c12s12n12a12m12e12\12e12n12d12c12s12n12a12m12e12\csname\endcsname␣
\string
\escapechar
\escapechar
\escapechar
\escapechar=65
\expandafter\string\csname\endcsname
A12c12s12n12a12m12e12A12e12n12d12c12s12n12a12m12e12A
\escapechar=-8
\expandafter\string\csname\endcsname
c12s12n12a12m12e12e12n12d12c12s12n12a12m12e12\string
\string
除了⟨控制序列 代币⟩还有另一种⟨控制序列⟩:⟨活跃角色标记⟩。
一个⟨活跃角色标记⟩是⟨字符标记⟩
其类别代码为 13(活跃)。⟨活跃角色标记⟩可以像⟨控制序列标记⟩。
例如,之后\catcode`\b=13
你可以做\def b{The active character \string b is a macro now.}
另一种划分方式⟨控制序列⟩是通过观察它们的“可分解性”:
TeXBook 说:
TeX 的约 300 个控制序列被称为原始序列;这些是低级原子操作,不能分解为更简单的函数。所有其他控制序列均已定义最终,就原始操作而言。例如,\input 是原始操作,但 \' 和 \" 不是;后者是根据 \accent 原始操作定义的。
(不应忽视,在定义宏的⟨控制序列⟩时,⟨非活动字符标记⟩也能发挥作用。)
还有另一种划分方式⟨控制序列⟩是通过查看它们触发的操作以及触发这些操作的时间点:
TeX 的输入处理和消化过程之间存在类似性:
- TeX 输入被分成所谓的 token(⟨控制序列标记⟩和⟨字符标记⟩) 在 TeX 的嘴里。
- 扩展可扩展 ⟨控制序列⟩发生在食道中。
- 不可扩张 ⟨控制序列⟩将在胃中处理。
所以你区分可扩展 ⟨控制序列⟩和不可扩张 ⟨控制序列⟩。
可扩展⟨控制序列⟩(及其参数)将在喉咙中被其他符号星座所取代。可扩展的⟨控制序列⟩发生在食道中,直到没有更多的可扩张⟨控制序列⟩左边。
不可扩张⟨控制序列⟩(在 TeX 口中的标记化过程中产生,或在扩展过程中通过替换可扩展标记而产生)通过食道到达胃部,在此构建盒子、执行分配原语等。
有原始的⟨控制序列⟩可扩展的:
例如,\string
-primitive 是一个可扩展的原语。它与以下标记一起消失在 TeX 的咽喉中,作为替换,你会得到一组⟨字符标记⟩类别 12(其他)或 10(空格),代表⟨控制序列标记⟩有问题/是 catcode-12-pandant 的⟨字符标记⟩有问题。(当在 TeX 的标记流中,显式空格标记位于控制字标记后面时\string
,扩展\string
将产生一个显式空格标记。)
例如,\csname
-primitive 是一个可扩展的原语。它触发 TeX 的 gullet 中 -expression 的处理\csname..\endcsname
,然后它在 TeX 的 gullet 中消失,作为替换,你会得到相应的⟨控制符號⟩或者⟨控制字标记⟩如果该标记未定义,它将获取\relax
当前范围内的 -primitive 的含义。
例如,\romannumeral
-primitive 是一个可扩展的原语。它和以下⟨数字⟩消失在 TeX 的喉咙里,以防⟨数字⟩为正数,作为替换,你会得到一系列⟨字符标记⟩类别代码 12(其他)代表相应的⟨数字⟩以小写罗马字母表示。
有原始的⟨控制序列⟩不可扩展的:
例如,\relax
-原语是不可扩展的。它确实会通过食道到达胃部。
例如,赋值原语\def
、\edef
、\gdef
、\xdef
等\countdef
不可扩展。它们不会在 TeX 的食道中触发替换,但会到达胃部。
有非原始⟨控制序列⟩可扩展的:
宏是非原始的⟨控制序列⟩可扩展。它们和它们的参数被替换为它们的⟨替换文本⟩在 TeX 的胃中。宏在 TeX 的胃中根据原语\def
、\edef
和进行定义。请注意,LaTeX 的also 只是一个宏,它在 LaTeX 的胃中“分解”为包含 -primitive 的一些标记\gdef
集。\xdef
\newcommand
\def
有非原始⟨控制序列⟩不可扩展的:
例如,您可以做类似的事情\let\bgroup={
。
\bgroup
是⟨控制序列⟩. 更具体地说:这是一个⟨控制序列 令牌⟩。 它是一个⟨控制字标记⟩。它不可扩展,但也不具备 TeX 中大约 300 个不可分解函数(称为原语)之一的含义。\bgroup
。隐式字符标记。 ;-)
其他类型的非原始⟨控制序列⟩不可扩展的有,例如,\chardef
-tokens、\countdef
-tokens、\toksdef
-tokens、...
为了熟悉这些术语,让我们来解析一些案例:
完成任务后
\catcode`\W=13 %
\catcode`\X=13 %
\catcode`\Y=13 %
\catcode`\Z=13 %
\newcommand W{The active character \stringW is a macro.}
\newcommand\something{This is a macro.}
\newcommand\+{This is a macro, too.}
\let X=a
\let Y=\relax
\let Z=\romannumeral
,
W
是⟨控制序列⟩: 它是一个⟨活跃角色标记⟩。它是一个宏,因此既可扩展,又不是原始的。X
是⟨控制序列⟩: 它是一个 ⟨活跃角色标记⟩。它不可扩展。因此它不能是宏。它也不是不可扩展的原语。它与 catcode 11(字母)具有相同的含义 ⟨字符标记⟩a
. 它是一个隐式字符标记。Y
是⟨控制序列⟩: 它是一个⟨活跃角色标记⟩。其含义等同于不可扩展原语的含义\relax
。因此,它是一个不可扩展原语。Z
是⟨控制序列⟩: 它是一个⟨活跃角色标记⟩。其含义与 expandable 基元的含义相同\romannumeral
。因此,它是一个 expandable 基元。\something
是⟨控制序列⟩: 它是一个⟨控制序列标记⟩。 它是一个⟨控制字标记⟩。它是可扩展的。它的含义不等同于 TeX 中大约 300 个不可分解的函数(称为原语)。它是一个宏。\+
是⟨控制序列⟩: 它是一个⟨控制序列标记⟩。 它是一个⟨控制符號⟩。它是可扩展的。它的含义不等同于 TeX 中大约 300 个不可分解的函数(称为原语)。它是一个宏。
术语“宏”始终表示非原始可扩展⟨控制序列⟩。
因此,当你知道一个 token 是一个宏时,你可以得出结论,它不可能是一个
⟨非主动角色 令牌⟩因为⟨非主动角色 代币⟩不可能是⟨控制序列⟩。
你可以得出结论,这是一个⟨控制序列⟩。
但你不能断定⟨控制序列⟩是⟨控制字 令牌⟩或⟨控制符号 令牌⟩或
⟨主动角色 令牌⟩。
并非所有都可扩展⟨控制序列⟩是宏。
有可扩展⟨原始⟩也一样。
并非所有不可扩展⟨控制序列⟩是⟨原始⟩。
还有不可扩展的非原始类型:
例如,隐式字符标记,\chardef
-代币,\countdef
-代币,\toksdef
-代币,...
答案2
宏编程是一种编程风格(不同于编译程序),其中操作通过每个标记被其内联替换来实现替换文本. 除了TeX之外流行的宏编程语言还有C语言预处理器和m4系统。
TeX 的主要用户定制模式是通过其宏处理语言,但当然它需要一些可用于构建宏的内置命令(原语)。
因此诸如\def
、、\halign
之类的命令\count
不是宏。
Tex 也有一些基元被归类为可扩展因此它们的作用与预定义宏非常相似,尽管从技术上讲它们在 TeX 中不属于宏,但这些可扩展原语的列表如下:
是否有可扩展的 TeX 基元列表?LaTeX?e-TeX?其他?
控制序列是一串字符,例如\foobar
引用命令(或标记,或未定义),而不是引用自身的字符,例如x
仅引用自身的字符,它可能引用 tex 基元或宏,具体取决于上下文。请注意,宏是标记含义的属性,而控制序列是其语法的属性。因此默认情况下是宏,~
但它是活动字符标记,而不是控制序列。
在 TeX 中,宏总是由\def
及其变体定义,例如\edef
。LaTeX 定义命令例如\newcommand
本身就是宏,最终导致应用 来\def
定义宏。
答案3
答案4
很大程度上取决于你如何使用 LaTeX。如果你是一名作者,你并不真正关心宏、控制序列、标记等。从这个角度来看,任何以某事开头\
并执行某事的东西都是命令。在较小程度上,你会担心长度和粘合(原始 TeX 语言中的尺寸和跳跃),尽管给定一个适当的文档类,作者不太可能使用其中任何一个,可能的例外\linewidth
或\baselineskip
用于导入图形的缩放目的。
另一方面,如果你以开发人员的身份接触 LaTeX 并创建类或包,你会更加关心,尽管 expl3 确实抽象出了很多内容(尽管偶尔会滥用术语)。² 一切仍然是由底部的原语构建的宏,但至少有一个一致的术语,只要你能够越过 LaTeX 来源的三级考古学³ 来理解事物的工作原理。
可以编写一个可以分配给 expl3 值的 expl3 函数,但这不是默认行为,并且 expl3 没有固有的返回类型概念,因为它本质上仍然是宏扩展。
在其他语言中,如果你将 map 应用于某个对象,则会得到同一类的对象(因此,例如在 Rust 中,
iter.map(|x| x.to_string())
会采用某种类型的迭代器iter
并将其转换为该类型的字符串值上的迭代器,并将some_optional.map(|x| x*x)
可选值转换some_optional
为保存其内容平方的新可选值(如果存在)),而_map
expl3 的各种函数只是迭代它们的值,但实际上并不返回新的tl
,prop
,str
,而是产生它们的输出就地. 也许更令人担忧的是 Expl3功能在大多数情况下¹任何其他语言称之为函数,但更正确地说是过程。所有这些都不是抱怨(太多)。 Expl3 是一项巨大的成就,它与 xparse 一起提供了一种方法,使以前困难的事情变得更容易完成。 TeX 是一种真正独特的编程习语,它的管理在很多方面类似于下棋,因为最直接的路线往往不会产生预期的结果。
即、使用 Expl3 编写的较新的代码、使用带有
@
语法的私有 LaTeX 命令的旧版⁴代码以及最后采用原始 TeX 语法的最低级别代码。当某些东西占据了代码的大部分时,将其称为遗留似乎有点奇怪。