人们普遍认为,在大多数编程语言中,宏不是一个好主意。(例如,参见http://scienceblogs.com/goodmath/2007/12/17/macros-why-theyre-evil/。
话虽如此,我有一种印象,宏不仅仅存在于 LaTeX 中,这是出于历史原因——似乎排版领域的某些属性使得宏成为合适的工具。(这似乎与使用 LaTeX 是非常与主流语言编程不同。)有人能解释一下为什么会这样吗?或者,确实有人认为宏是历史遗留的产物?
编辑:这并没有回答问题,但密切相关: https://tex.stackexchange.com/a/42749/17049
答案1
首先,应该说,与您链接的博客文章相比,您在 TeX 中没有选择。
该博客认为,使用宏作为完全图灵完备的语言在之上真正的编程语言、宏和执行语义可能会互相干扰(尽管我认为第一个使用的例子,C 构造i++
是一个应该从地球上消灭的躯体)。
然而,在 TeX 中,宏和执行语义“按设计”紧密交织在一起。例如,由于所有条件都是基于扩展的,因此你不会得到尾递归而不考虑扩展语义。
所以问题归结为将 TeX 设计为基于宏的语言是否是一个好主意。
这里有两点需要考虑。
首先,从历史的角度来看,我不相信 TeX 的宏功能是为了编程很多复杂的东西。我认为 Knuth 基本上设计 TeX 是为了“开箱即用”地做大多数事情,宏主要提供
- 有点抽象,
- 处理状态(例如章节编号)
- 中度至重度额外编程,如输出例程(只需查看输出例程即可
plain.tex
了解该东西的样子)。
只要看一下下面的宏包就可以找到证据:
plain.tex
:1241行manmac.tex
:715 行gkpmac.tex
:890行
这应该大致就是 Knuth 所做的宏编程的数量......
为了进行比较,这里有一些来自 LaTeX 领域的数字:
latex.ltx
:8003 行hyperref.sty
:8563 行- 全部 TikZ:超过 300000 行(粗略估计)
我认为 TeX 的宏本来就不是用来做这个的......
其次,从现在的角度来看,问题是宏语言是否适合当前的任务。
我知道在这方面人们的看法大相径庭。很多人绝对讨厌用 TeX 编程的方式。我不确定是否有人真的喜欢它……
我希望双方都能在这里发言,因为我想再次详细了解一下争论的细节。
就我个人而言,我对编程范式有点不了解,所以我倾向于将宏编程视为另一件需要处理的事情。宏编程范式主要有两点让我很恼火:
- 很容易完全且几乎不可挽回地弄乱您的代码。诸如忘记强制宏参数或忘记条件构造的一部分(甚至忘记正确位置的空格字符)等简单的事情都可能以人类无法想象的方式改变代码的语义。
- 因为没有真正的指针构造,一些操作(比如将一个元素添加到线性列表中)往往会获得比其应有的更高的计算复杂度(因此将一个元素添加到线性列表中将需要在)时间,积累到O(n²)为了n操作,而它应该只需要在)完全)。但我不完全确定这是否完全归因于宏编程或只是 TeX 中缺少数据结构。
另一方面,原则上,宏执行作为整个“排版系统”范式的一部分相当方便。文本“自行排版”,你不需要用语句明确打印它printf
,宏散布在中间,扩展到更多文本,这使得人们很容易集中精力创作编写文档时执行的任务,这毕竟是 TeX 的全部内容。
我一直在思考,在排版系统中,什么可以替代宏来增强文本,但是没有想到什么令人信服的方法。
已经有人尝试将 TeX 的排版功能与不同的编程范式结合起来,但我对其细节并不了解。希望其他更了解这些尝试的人也能回答。
目前,LuaTeX 似乎提供使用原始 TeX 排版的功能,同时在 Lua 中进行所有编程。这里有几个常客就是这么做的,希望他们能分享一些他们的经验。
答案2
当今大多数编程语言都是分层定义的:文本被分解为标记,这些标记大多由包括一组固定关键字的正则表达式定义,标记构成上下文无关语法的基础,上下文无关语法从标记流构建抽象语法。作为语法一部分的标识符具有静态作用域。抽象语法的静态语义部分由类型系统定义,类型良好的程序具有动态语义...
当 Knuth 在 70 年代研究 TeX 时,这种清晰度根本不存在,我将他的一些设计决策归功于当时最先进的技术,同时仍然承认文档处理语言与编程语言的用途不同。例如,今天没有人设计一种没有固定关键字集或某种模块系统的语言,而 TeX 的 catcodes 允许重新定义语言的语法,模块系统根本不存在。
这鲁特系统是重新思考 TeX 的练习,Unix 操作系统。因此,Lout 具有比 TeX 更严格的静态和动态语义,但我认为尽管 TeX 有种种怪癖,但这种差异太小,不足以超越 TeX。