![简洁版本](https://linux22.com/image/364434/%E7%AE%80%E6%B4%81%E7%89%88%E6%9C%AC.png)
简洁版本
为什么到了 2017 年,LaTeX 不使用树状结构,而是选择处理丑陋的标记重写规则?
长版本
LaTeX 诞生于近 35 年前。也许在那个时候,函数式编程语言还不太为人所知,使用/实现起来太复杂,或者其他原因,但为什么到了 2017 年,LaTeX 编程仍然如此复杂?
在我看来,有几件事让它的使用变得复杂:
计算很难:如果你只想将两个浮点数相乘,你需要通过
pt
在末尾添加字母将它们转换为维度,并处理乘法后它不是维度的事实,所以你需要除以 1pt,然后你需要在浮点数中进行计算,并希望你的浮点数不要太大,因为 LaTeX 无法处理高于的维度16383pt
,最后你要去掉 'pt' 字符...此外,如果你想做一些涉及括号的计算,比如(1.56*56 + 5.1*12.42)*(1.2*48 + 5)
你将需要使用大量新的长度变量,因为\dimexp
无法处理这种计算。对于一个简单的乘法,完全疯了。此外,你不能使用太多的寄存器,因为计数器的数量被限制为 255(我同意,使用 etex 扩展,你可以得到 32767 个计数器,但它不是那么大)。好吧,你会告诉我“使用 lualatex”。对于某些东西来说这可能是正确的,但它并不能解决所有问题。标记的使用并不自然。因为您需要解释何时需要扩展标记,所以您的代码将包含大量的
\expandafter\expandafter\expandafter
,\noexpand
... ,这使其完全不可读。当你想做高级演示时,99% 的时间你都需要使用疯狂的技巧来实现你想要的,这需要对元素的编码方式有很好的理解。例如,我甚至不确定是否可以在页面末尾添加给定的代码,例如在所有页面的中间放一个大印章(但我可能是错的)。
我认为这些问题大多源于 LaTeX 仅使用“标记重写规则”这一事实:它总是重写一大串标记,而对我来说,表示文档的自然方式是使用树状结构,例如,可以使用 Haskell 或 Ocaml 等函数式语言轻松创建。这正是著名的 HTML 所持的观点!它可能比 HTML 复杂一点,特别是因为您需要处理页面,但对我来说,使用树状结构来处理文档更自然。然后,我们可以想象所有函数都将文档树作为参数,然后对其进行树重写,生成另一个文档树,然后可以由另一个函数处理,依此类推。要知道什么时候应该使用哪个函数,我们可以例如定义几个这样的大步骤,它们将代表将文档切分为页面的自然过程,并将每个函数分配给给定的步骤。如果你需要更精确,你可以说“要运行我的函数 F,我需要有函数 G 的结果,并且我需要在函数 H 之前运行”。或多或少类似于 Linux 初始化程序systemd
(好吧,至少我认为它是这样的)。我很确定用 LaTeX 编写复杂的东西会更自然!
所以我的问题是:为什么在 2017 年,LaTeX 不使用树状结构,而是选择处理丑陋的标记重写规则?
答案1
简短的回答:这只是 TeX 的编写方式,没有人写过更好的东西,正如@percusse 的回答。
有关“为什么在 2017 年,LaTeX 会……”中可能存在的误解的详细答案(关于我们在谈论什么、正在开发什么和未开发什么、已经完成或即将完成什么),请参见下文。这个答案太长了,所以需要一个目录。:-)
- TeX 的稳定性
- LaTeX:文档作者的宏
- TeX 扩展和其他系统
- 概括
1. TeX 的稳定性
LaTeX 诞生已有近 35 年
TeX(该程序,而不是宏包 LaTeX)于 1977 年设计,并开始流行,35 年前的 1982 年重写。此时,Knuth 宣布 TeX 已稳定,他只会修复错误,不会做进一步的更改或添加功能(他需要回到他的实际工作中),但其他人可以自由地使用他的代码并编写新系统。
我现在找不到 Knuth 在 1982 年宣布 TeX 稳定的参考资料,但你可以看到他在详细描述 TeX 的故事时的一些评论1986年他在 1989 年确实将 7 位改为 8 位,当时你可以看到他说1989年那
五年多来,我始终坚信:一个稳定的系统远比一个不断发展的系统好。
和1990年那
我开发 TeX、METAFONT 和 Computer Modern 的工作已经结束。除了纠正极其严重的错误外,我不会再做任何更改。[…] 我坚信不变的系统具有巨大的价值,尽管任何复杂系统都可以改进,这是不言而喻的。因此,我认为对 TeX 和 METAFONT 系统进行进一步“改进”是不明智的。让我们将这些系统视为固定点,它们在 100 年后应该会产生与今天相同的结果。
此外,关于其他人如何构建更好的系统:
我将这些系统置于公共领域,以便世界各地的人们可以根据需要自由使用这些想法。[…] 任何人都可以以他们想要的任何方式使用我的程序,只要他们不使用 TeX、METAFONT 或 Computer Modern 的名称。特别是,任何想要制作比我的程序更好的程序的个人或团体都可以自由地这样做。[…] 当然,我并不声称已经找到了解决所有问题的最佳方法。我只是声称,将固定点作为构建块是一个很大的优势。[…] 我欢迎继续研究,以期找到可以比 TeX 更好地排版文档的替代系统。
他详尽地记录了程序的行为(通过TeXbook) 和该程序本身(通过一种他称之为文学编程的方法,并以书的形式出版)TeX:程序),并给出会谈TeX 程序的内部结构。后来他甚至教了一门课程以程序源代码作为教材。因此,TeX 可以说是有史以来文档最齐全的同类程序,其他人可以在其基础上构建新系统。
然而,由于各种原因(例如,也许整个程序是单片的,并且文档假设您想要全面了解程序,直至最小的细节:这只是 Knuth 的想法),编写扩展/修改程序的人比 Knuth 想象的要少。参见自 1996 年起:
弗雷德:我听到您说过,您期望有更多的人扩展 TeX,但实际上却没有。
得可:是的,当然。每当有人有特殊用途的重要项目时,我都会期待延期,比如大英百科全书或编写一本阿拉伯语-中文词典,或者其他什么——一个大项目。我从未想到一个工具能够处理所有人的奇特项目。所以我在代码中构建了很多钩子,这样计算机科学毕业生就可以在一周左右的时间内相当轻松地为特殊场合设置一个新程序。这是我的想法。但我认为人们很少这样做。
这绝对是我会做的事![…] 重写排版系统相当容易。[笑声]
我尝试通过实现 TeX 的几个功能来演示如何做到这一点,就好像它们是后来添加的一样,只是为了展示如何使用钩子,作为一个演示。但这并没有让事情开始。因此,越来越多的人在宏观层面上使用 TeX。当然,最大的优势是你可以与其他人分享你的输出——你可以假设它会在其他人的系统上运行。但我仍然认为特殊项目会导致大量程序的自定义版本。这并没有发生。
和再次:
?:我想问一下关于使用 TeX 源代码部分的问题。您明确表示,程序员可以自由地将 TeX 源代码部分合并到自己的程序中。[…]
得可:我认为 TeX 的特殊版本会相当普遍。我设计 TeX 时,它里面有很多钩子 […]
宏语言是图灵完备的——它可以做任何事情——但是,如果在低级语言中做任何事情都很容易,那么尝试用高级语言做任何事情肯定是愚蠢的。因此,我为 TeX 构建了钩子,并实现了 TeX 的某些部分作为这些钩子的演示,这样阅读代码的人就可以看到如何将 TeX 扩展到其他领域。我们预计某些化学或制作更改栏的事情将用机器语言来完成,以用于特殊应用。
这正是我所期望的。当然,在 80 年代中期,世界上有超过一千人知道 TeX 程序,并且非常了解 TeX 程序的复杂性。他们读过 TeX,并且如果他们愿意,他们能够制作任何扩展。现在,我想说,了解 TeX 内部原理的人可能不到一千人,一百多人。它的发展还没有达到我预期的程度。
因此,对你的问题的部分回答是,没有足够多的人构建足够新的系统(我们稍后会看到现有的少数系统);你基本上仍在使用 1977 年设计的程序,而不是 2017 年。Knuth 显然高估了他的代码的可读性,或者其他人阅读和扩展他的伪 Pascal 源代码的意愿和能力。
LaTeX:文档作者的宏
当对 TeX 的需求出现时,Knuth 已经准备好并完善了内容(这是 TeX 第 2 卷的第二版)。计算机编程艺术),和他以第一版作为书籍设计的参考。他只需要排版,并构建了 TeX 作为计算机工具,在热金属排版排版员(排字员)手工排版,或者使用 Monotype 等特殊机器。因此,它具备印刷精美书籍所需的许多功能。其基本功能包括从不同字体中挑选字符并将它们放在页面上的不同位置、升高和降低字符、在这里留出一定量的空白、在那里分页,以及最有趣的是,将段落分成几行以获得令人满意的结果。
当你像字体排版师一样思考时,你会关心这些事情,这对 Knuth 来说很有意义:他用手写和润色内容,所以当他使用计算机将内容输入 TeX 时,是为了排版和控制外观,这只是他写书的全部工作中最后一小部分。对于大多数作者(可能从一开始就在电脑上打字),他们需要一个可以在整个文档准备过程中为他们提供帮助的系统,而在此期间,作者需要考虑的是他们的内容以及它的结构,而不是排版或外观。而且他们也不想为外观的设计而烦恼。
大约在这个时候,还有其他计算机文档制作系统,尽管这些系统都不够好(与当时的设备一起使用)来制作 Knuth 认为的“真正的”书籍。在贝尔实验室,特罗夫,谁的eqn
数学体系(出版于 1975 年)实际上是 TeX 数学语法的灵感来源。公共事务局,其中 Knuth曾使用过其中一个是隶: 看此 (10MB) PowerPoint 演示文稿,回顾1998它的语法如下:
@Chapter(Introduction)
@Section(Running Scribe)
@Begin(Quotation)
Let's start at the very beginning, a very good place to start
@End(Quotation)
等等。(大约在这个时候,与 Scribe 同步开发的是 GML 和 SGML,最终导致了 HTML 和 XML。)Leslie Lamport使用划线员,将这些想法带入了 TeX。通过他编写的宏包 (LaTeX),用户既可以获得 TeX 的精细排版,又可以获得 Scribe 的易用性,包括结构化标记、形式和内容分离、逻辑结构等。您可以在 Lamport 的文件制作:视觉还是逻辑?,这表明目标LaTeX 的目的是使格式化变得更加困难,而逻辑结构变得更加容易。
在我看来(2017),LaTeX 既是一个真正伟大的想法(因为它符合作者的想法或应该的想法),又是一个错误(因为它是用 TeX 宏编写的,而不是更合理的编程语言)。
我们可以想象一个平行宇宙,其中 LaTeX 是用一种高级语言编写的,它接收你的输入文件,将其表示为一个逻辑结构(类似于抽象语法树),然后对树进行转换以生成原始 TeX 标记,然后将其输入到 TeX 引擎,后者只进行排版。如果你想改变它的工作方式,你可以选择调整在各个阶段发生的转换。我认为当你写的时候,你问的应该是这样的:
我们可以想象所有函数都将文档树作为参数,然后对其进行树重写,生成另一个文档树,然后可以由另一个函数进行处理,依此类推。
确实,我们可以想象。尽管标记结构化,但很难从 LaTeX 可靠地转换为 HTML 等其他格式,原因就在于它不是这样写的:尽管标记结构化,但任何单个标记字符串的含义都取决于 TeX 排版引擎的各种细节、已加载的软件包/定义的宏以及各种“状态”。
相反,LaTeX 是用 TeX 宏“语言”编写的(它最初甚至不打算成为一种编程语言,而只是作为一种简单的文本替换和快捷方式来节省打字时间),使用了一些巧妙的技巧(请参阅它们的一些前身一些 TeX 编程技巧由 Lamport 于 1982 年发明)来模拟高级编程结构。当时有充分的理由:没有广泛使用的标准编程语言(每台计算机都有自己的操作系统和一套支持的编译器和语言),TeX 宏保证可以工作,而且人们已经开始将宏推向超出其应有的范围(甚至 Knuth 从他的第一个设计开始)。因此,它的实现也反映了当时的限制。Lamport 本人声明“不再有新功能”1985 年的 LaTeX 2.09 就是一个例子,尽管后来有了 LaTeX2ε。后来对 LaTeX 的研究,例如 expl3,更进一步,使底层的宏越来越复杂,同时为用户提供越来越清晰的界面。从某个角度来看,这种理念是有道理的,但从其他角度来看则不然。
一旦有大量经过充分测试的 LaTeX 代码(拥有数十年的使用经验),并且以不破坏用户现有文档为目标,即使有人愿意,完全重写系统也会变得越来越困难。即使存在 LaTeX 开发团队,他们专注于 LaTeX3 并为用户提供更好的服务,而不是重写所有内容并冒着损坏的风险。大多数 LaTeX 用户只是用户(不是程序员),因此优化以满足他们的需求是有意义的,而不是为了实现的整洁。
我认为您提到的几个问题仅仅是重复同样的错误/误解:
为什么都 2017 年了,LaTeX 编程仍然如此复杂?
1)计算很难完成[…]将两个浮点数相乘[…]
2)使用 token […] 你的代码将包含大量的
\expandafter\expandafter\expandafter
,\noexpand
... 这使得它完全不可读
那为什么是你想在 TeX/LaTeX 上编写程序吗?它是一个带有文档创作包的排版系统,而不是一个带有高质量警告、调试器等等。如果你意识到这些问题并且不介意它们甚至喜欢挑战(这个网站上有很多这样的人),那是一回事。但对于其他人来说,没有什么可以阻止你在其他地方(使用“真正的”编程语言)进行所有编程,并将输出输入 (La)TeX。事实上,这就是我推荐的。抱怨 LaTeX 不是一个好的编程环境是没有意义的,因为它并不是一个好的编程环境:它只是一个用于文档创作和排版的系统,而且它可以做得很好。
另外,当你说
3) 当你想做高级演示时,99% 的时间你都需要使用疯狂的技巧来实现你想要的,这需要对元素的编码方式有很好的理解。例如,我甚至不确定是否可以在页面末尾添加给定的代码,例如在所有页面的中间放一个大印章(但我可能是错的)。
这仅仅是没有学习排版系统 (TeX) 的表现。这两件事在 TeX 中都很简单(“印章”可能涉及 DVI 或 PDF 特殊功能,您需要知道如何编写,但将其放在页面上不是问题),尽管在 LaTeX 中您当然可能需要努力理解宏层,但这就是 LaTeX 的全部意义所在:为您提供结构而不是控制格式。
TeX 扩展和其他系统
让我们回到之前的观点,关于编写基于 TeX 的新程序(扩展它,或使用它的思想并编写一个全新的程序)。
有人尝试过,甚至做过。在 20 世纪 70 年代末,当时很少有跨平台编译器(因此也没有那么多语言),程序员通常会拿别人的程序,通读一遍,然后“移植”到新的语言或系统上。人们确实读过并理解了 TeX 程序,并用 C 等新语言编写了它。(我说的不是像 这样的自动翻译web2c
,而是像 CommonTeX 这样的手写实现。)在后来的几十年里,这种做法普遍减少了:如今大多数程序员宁愿使用别人的库并在其基础上编写自己的代码,也不愿读完并重写它。
尽管如此,还是有少数人修改了 TeX 程序,如果你看看那些留存下来的程序,你会发现它们中的大多数都是为了解决特定的用户需求,而不是从改进实现或使编程更容易的角度来编写的:
- Peter Breitenlohner 将 TeX 扩展为 eTeX(更多寄存器等)。(他的讣告“DEK 曾评论说 Peter 可能比他自己更了解 TeX 代码。”)
- Hàn Thế Thành 将 TeX 扩展为 pdfTeX,以便直接生成 PDF 输出,而不是 DVI 输出。后来,这些扩展也合并了 eTeX 扩展。今天,在像 TeX Live 这样的典型发行版中,当您运行 LaTeX 时(即使您将其作为
latex
DVI 输出而不是运行pdflatex
),运行的程序是 pdfTeX。 - Jonathan Kew 将 TeX 扩展为 XeTeX,以便系统可以原生地使用 Unicode 和系统字体进行排版。
所有这些人都通过编辑tex.web
来制作他们的扩展。要了解这涉及的内容,您可以阅读tex.web
(可从格式整齐甚至可以作为一本书,这要感谢 Knuth 的《文学编程》 (Literate Programming),也许可以作为一本书,或者etex.ch
也pdftex.web
可以xetex.web
作为一本书,然后尝试自己修改它,这样你就能理解它的复杂性。(如果你要把它变成一个程序,一定要给它起一个新名字。)
然而,有一个系统确实试图重写 TeX,纯粹是为了改进实现,以便它能够使用更现代的编程实践,更容易修改等。这就是 NTS,新的排版系统,用 Java 完全重写 TeX。根据 Wikipedia 页面,它于 1992 年开始,编码于 1998 年开始,并于 2000 年完成。完成后,虽然在某个时候作为一个成功故事呈现,但在实践中它失败了:它太慢了,而且无法运行 LaTeX 文档(也没有 eTeX 和 pdfTeX 扩展),所以没有人开始使用它。Java 化带来的预期好处并没有实现。(实际上NTS 现已可用随着计算机速度的提高,但……没有明确的理由使用它。有人说它忠实地再现了 TeX 的大部分问题,只是包装在 Java 类中。参见一些讨论这里,但请注意那里有很多 Usenet 论战,而且热度大于光亮。)
还有一些其他排版系统根本不使用 TeX 源代码,但使用了它的一些想法(有些比其他的多):鲁特,帕托林,西莱。但我怀疑,任何不能与现有 LaTeX 软件包兼容的排版系统,在涉及技术排版的已经非常小的受众(学术界等)中,都很难获得采用。
这给我们带来了另一个主要实现:LuaTeX。它始于一个手工翻译的 C 版本的 TeX(CXTeX也可以看看LuaTeX 与 Pascal 告别),并且已经朝着问题中提到的方向发展了:
将每个函数分配给给定步骤。如果您需要更精确,您可以说“要运行我的函数 F,我需要有函数 G 的结果,并且我需要在函数 H 之前运行”。或多或少像在 linux init 程序中一样
systemd
(好吧,至少我认为它是这样的)。我很确定用 LaTeX 编写复杂的东西会更自然!
好吧,我不能说 LuaTeX 就是这样工作的,但 LuaTeX 确实有针对排版各个阶段的钩子,这可以让事情做得更优雅。(此外,与 TeX 中的宏/标记重写系统相比,Lua 是一种更传统的语言。)以下是一些我发现它很有用的例子:
- 解析 HH:MM 时间:使用 Lua 可以避免担心 token 扩展
- 编码之间的转换:我确信这项任务(编写 UTF-8 解码器、从文件中读取表格等)也可以在 TeX 宏中完成,但在 Lua 中更自然
- 避免在换行处使用短单词:使用适当的钩子来影响换行
pre_linebreak_filter
比使用 TeX 宏更容易。 - 找到排列:一些单词匹配编程
- 循环遍历字符:查找 catcodes(实际上不需要 Lua)
- 生成圆周率的数字:使用 TikZ 制作精美图片
- 如果可能的话适合部分:简单的参数减法(实际上不需要 Lua)
其他人有很多更好的例子,随着越来越多的人习惯了 LuaTeX 及其可能性,我相信我们将朝着更优雅、更易读的代码发展,这些代码会在正确的时间(在正确的钩子中)做正确的事情。(也就是说,我们不必弄清楚如何使用宏进行设置,以便最终当 TeX 达到我们关心的正确操作阶段时,它会执行我们想要的操作,而不会对早期或后期阶段产生任何不良影响——大多数 token 不适都来自于此。)原则上,您已经可以完全以编程方式使用 LuaTeX 生成文档:请参阅没有 TeX 的 TeX。
但 LuaTeX 仍在开发中(见其路线图),而且我们还没有看到它的结局(我希望)。
概括
- 您使用的 TeX 程序的设计与 1977 年的设计基本相同。
- LaTeX 是用 TeX 宏编写的,不是一个很好的编程环境,无法执行诸如整个文档的树形转换之类的奇特操作。
- 今天的 LaTeX 是基于 20 世纪 80 年代中期的版本构建的,并且开发主要是累积性的,而不是彻底的重写或重构。
- 有些人尝试过重写 TeX 程序,但是很少有人成功。
- LuaTeX 是一个“新”程序,它可以让您更优雅地完成工作、参与转换,并且编写很少需要担心标记扩展的代码。
- 目前情况还不理想,但可能会有所改善。
- 您可以在“外部”进行编程并将输入提供给(La)TeX;这样您就可以根据需要使用强大的工具。
答案2
因为 TeX 就是这样设计的。每个人都希望拥有一种更现代的语言,但总得有人用现代的方式编写它。
答案3
TeX是 35 年前设计的。LaTeX 是唯一使用 TeX 的宏包。你在我们的问题中提到的所有内容都与TeX,没有 LaTeX。请删除问题中所有出现的前缀“La”。
为什么今天没有更好的印刷系统?因为没有人创造它。曾经有过尝试(采用印刷过程编程的一般新概念),但没有成功。今天只有保守的扩展:LuaTeX、XeTeX。
答案4
tex 的创建者已下令 tex 不会改变,并且某种东西要想被称为 tex(并且 latex 被认为与 tex 紧密相关),它必须通过某项测试(“ trip
”测试)。
关于 latex,对于采用输入语言和创建另一个可产生等效输出的程序没有任何限制,并且有几个这样的程序(但据我所知没有一个可以处理 (la)tex 可能出现的全部复杂性)。
正如其他答案所述,有人只需创建这样一个程序,但它不能被称为“tex”,而且我认为如果它被称为“latex”,人们会感到非常愤怒。