(很抱歉,很长的答案指向与网站范围不同的方向:坦率地说,我很惊讶地首先看到这里的问题……。)
TeX设计用于排版,而不是用于编程。因此,当被视为编程语言时,它充其量是“怪异的”。
—唐纳德·努斯(Donald Knuth),数字印刷术,第235页
在过去的几年中,我读了很多关于TeX的早期历史(大约1977年)的书,还读了很多Knuth的著作。我的结论是,当我们谈论“ TeX(作为一种编程语言)”时,已经有些问题了。
如果我们看一下以前写的TeX的早期“设计文档”(请参阅TEXDR.AFT
和TEX.ONE
,发表在Digital Typography上),很显然Knuth正在设计一个主要用于排版计算机编程艺术的系统(他说过(例如,在这里)) (他想到的主要用户是他本人和他的秘书),并且认为,如果对其进行适当修改,它可能会更普遍地有用。为了节省键入的内容,对于重复要做的事情(例如,每次TAOCP需要包含作者的引文时,您都希望垂直移动一定量,设置特定的跳行,选择特定的字体,引用右对齐,选择另一种字体,排版作者的名字...),其中有宏。
您可以猜测其余的。在TeX中,我们遇到的是“偶然的图灵完成”(更多)案例,只是它发生在(不幸的)社区(计算机科学家和数学家,DEK本人也要“怪”)社区中太聪明了,不能忽略这一点。(传奇人物迈克尔·斯皮瓦克(Michael Spivak)遇到TeX之前从未编程过,但是他对它如此着迷,以至于他最终编写了AMS-TeX,这是当时存在的最复杂的宏集之一。)因为编写了TeX为了能够在大量系统上移植(在当时是很大的事情),在TeX中总是有做所有事情的诱惑。此外,由于他的编译器编写经验,Knuth像编写一个编译器一样编写TeX,并偶尔将其描述为一个。如果在您的输入上运行的程序是“编译器”,那么您肯定在编程,对吗?
在此答案中,您可以读到更多有关Knuth不打算在TeX中进行任何编程的知识,以及他如何“只有在大喊大叫之后才能使用TeX的许多编程功能” 。正如我所说,无论他的意图是什么,人们确实开始想出(滥用)TeX宏系统来完成令人惊讶的编程壮举的方法。克努特发现这个迷人的和(除了增加了一些功能集成到TeX的本身)包括其中的几个在附录d“搞鬼” 的TeXbook,但事实证明,尽管名称,是 “十分之九的例子其中有用于LaTeX的实现”。
让我换一种说法:LaTeX是Leslie Lamport在TeX之上编写的宏系统,它是一个很棒的想法。以语义,结构化,面向人的方式(而不是(Knuth)TeX的面向页面的方式(或如Lamport所说的那样,逻辑而不是可视的))来编写文档是一种很棒的方法。但是,在我看来,至少在今天已经完成的情况下,使用TeX宏而不是使用“适当的”编程语言来实现与LaTeX一样复杂的操作,介于巨大的错误和肆无忌per的行为之间。甚至Knuth都为人们不仅扩展TeX程序而不是在TeX宏中执行所有操作而感到震惊。
今天,有更好的方法来进行“编程”。您可以使用大多数人的计算机上广泛使用的多种语言中的任何一种来使用外部程序,也可以在Lua中使用LuaTeX和程序(并且比单独使用TeX宏做得更好,因为您可以操纵内部结构并正确级别的算法)。而且,如果您做对了,您可能会拥有比TeX宏中实现的程序更好或更快的程序。
从这个角度来看,使TeX中的程序更快的任务几乎很有趣,并且使我想起了描述另一种“偶然的图灵完整”编程“语言”的论文的最后几句话:汤姆·威登海恩(Tom Wildenhain)可爱的“ 论MS的图灵完整性”去年的PowerPoint(视频):
PPTXTM证明了PowerPoint开发的理论可能性,[…]。PowerPoint应用程序优化中也需要完成工作。利用PowerPoint自动缓冲下一张幻灯片的潜力很大,通过仔细放置幻灯片可以大大提高应用程序的性能。
立顿描述的轶事是说明性的。TeX不仅不存在形式语义,而且也不大可能存在。对于它来说,它太“奇怪”了,“语言”(正如我希望我已经在上面解释的那样)甚至都不是一种语言。例如,您可能认为您正在将宏作为函数编写,但是在其中引入了单个流水字符(甚至是空格),TeX立即将其视为排版指令。
简而言之:TeX会尽早恢复排版,并且在扩展宏时会勉强地这样做(急于进行其“实际”排版工作),这些扩展本身可以依赖于其中的数百种“状态” TeX的程序(像参数的值\hsize
或者\baselineskip
,箱子和其它寄存器...的内容),这就是为什么TeX的任何形式语义必然是东西,考虑到程序和它的所有内存的整个状态,直到我们最终以类似“ TeX代码的含义就是TeX所做的事情”之类的形式出现,其形式比TeX程序本身更复杂。
很好,(如果我已经说服您的话)TeX并非旨在作为一种编程语言,并且不能像真正的语言一样工作,没有正式的语义,并且今天有更好的编程方法,但是所有这些都无法帮助您实际的问题/问题是,在实践中,许多打算由TeX处理的文档确实使用了复杂的宏(例如LaTeX和TikZ),令人惊叹的庞然大物相互叠加。我们如何才能使其更快并设计出“优化通道”?
您将无法使用IMO的形式语义到达那里。我最近对此有所考虑,以下是一些初步的想法。
我的印象是,Knuth是1960年代经验丰富的编译器作家之一(这就是为什么他被要求写成《计算机编程艺术》的编译器书),而TeX(在许多方面)都是按照编译器的方式编写的。例如,写于1970年代。从那时起,编译器技术和设计得到了改善,TeX程序也可以如此。可以通过加快速度来完成一些事情:
从本质上讲,TeX的编写就像“解释性例程”一样,其中TeX的“眼睛”和“嘴巴”(其输入例程)将指令传递给其“胃”(其语义例程),并逐一执行。(您可以在TeX程序的第15部分中看到一个列表。)例如,当TeX的眼睛/嘴巴碰到\hfill
或\hskip
在其输入中时,肚子会收到一个“ hskip”命令,该命令会起作用。这类似于当今所谓的字节码解释器,并且在重构TeX程序以显式发出这些字节码/操作码时可能会有所价值,以便我们能够使用现有的(今天更为传统的)编译器技术。或至少缓存它们以避免重做工作。当然有很多挑战:
在“胃”中执行命令通常仍涉及读取输入,即输入例程和语义例程的工作不会在单独的阶段发生。例如,如果给出“ hskip”命令\hskip
(而不是说\hfill
),它将调用scan_glue
以从输入中读取胶水规格,这反过来可能涉及扩展宏等,直到找到足够的胶水标记,将输入堆栈保留在状态大不相同。
诸如eTeX和pdfTeX以及XeTeX和LuaTeX之类的引擎引入了新的命令和原语(实际上,每个人实际上都使用eTeX / pdfTex原语);您还需要支持它们,而不仅仅是原始Knuth的TeX程序中的支持。
我们可以做类似“推测执行”的事情,并行(使用多个内核)处理未来的段落(也许从自然的检查点,如新的节或章开始),跟踪它们使用的所有TeX内部状态(取决于),并抛出如果稍后我们发现前面的段落最终更改了该状态,则将其删除(并重做)。目前,TeX完全按顺序在1个处理器上运行;典型的硬件朝着不同的方向发展,并且有多个内核可用。
更简单的是,我们可以简单地通过输入文件的特定部分来缓存工作(已访问和修改了TeX的状态)。(我们可以在输入级别(扩展所有宏的最终结果)或在组装什么盒的级别,或者一直到程序总状态的级别进行缓存。)例如,内部的内容一个\begin{tikzpicture} … \end{tikzpicture}
不太可能像页码计数器那样在很大程度上依赖于TeX状态,因此,当我们重新编译TeX文档时,我们可以简单地重用所有工作-如果我们一直跟踪足够的信息以知道这样做是安全的。(当然,TikZ特别具有将其外部化并包括结果的方法,但是这个想法更笼统。)
我们可以使用技术(例如函数编程中使用的那些技术)来处理带有“空洞”的TeX-例如,现在,当您\ref{foo}
在LaTeX中编写以引用(例如将来)节号时,它仅在两个编译阶段有效:首先处理整个文档(所有段落都经过排版,页面上放置浮点等),然后将节号写到辅助文件中,然后第二遍全部处理再次完成该工作,这次实际可用的段号。(这种黑客攻击在当时可能是不可避免的,我知道对运行时间的影响是“只是一个恒定因素”,但是……。)相反,如果我们可以简单地处理带有“漏洞”的文档该怎么办(一个带有未确定内容但有一些估计宽度的框)留给节号,然后在文档处理结束时填充该框?(是的,我们的估计宽度可能会出错,并且段落可能需要重新处理,因此甚至页面也需要重新处理,但是我们可以根据需要执行工作,或者出于速度考虑接受一种允许错误宽度的模式部分编号。)
类似的技术也可以用于TeX文档的交互式编辑:当您编辑段落时,可以“实时”处理该段落,而以后的段落只需沿厨房向下移动即可(例如)。我们知道这是可能的,因为已经存在执行此操作的(商业)TeX实现,例如BaKoMaTeX和Texpad以及以前的Textures。(请参阅BaKoMa-TeX和类似TeXpad的主页上的视频,例如,此视频 -我尝试了后者,但实际上它实在令人难以忍受。
不要小看:向用户展示事物的价值,使TeX更易于调试。现在,用户只能看到他们的TeX输入,却不知道TeX到底在做什么,例如,它在段落的换行或宏扩展(以及哪些宏)上花了多少时间,它在组装什么盒子以及扔掉,哪个包正在写出什么特别的东西,等等。我(也许是乐观的)认为,有些用户希望看到此信息,并会发现它很有用,例如,知道他们正在使用的奇怪的阴影包在后台具有梯度的方程很便宜(几乎不增加处理时间)。通过查看在哪里进行了很多浪费的工作,他们可以将其中的一些扔掉(至少要等到最终的印刷运行之前)。(这有点像将概要分析信息插入程序的编译器或其他工具。)例如,使TeX更加透明和可调试可能会大大提高可用性。(如果我们使用的大多数TeX带有很少的宏,而不是LaTeX或当今大多数用户如何使用它,TeX在其IMO时代就已经非常易于使用和调试了。)
另外,将来的任何工作都应考虑(构建)LuaTeX,这是我们目前对TeX的最佳修改。
所有这些只是闲散的想法(我没有实施任何想法,以了解所需的努力或我们将获得多少提速),但是我希望这可以为您解答问题或为将来的方向提供思路。