我在 tex.se 上见过很多程序员,我想知道他们如何看待 TeX/LaTeX 作为一种编程语言与其他语言的关系,以及它是否会促进我作为初学者使用其他(非排版相关)语言的进步,这当然是抽象或语义意义上的。
答案1
我不太经常表达的观点是 TeX 是一个糟糕的编程语言,任何学习编程的人最好避免从中学习任何东西。事实上,拥有编程经验来编写 TeX 比相反的编程经验更有帮助。我想到了一些主要和次要的观点:
编写 TeX 时,必须非常注意代码中的确切字符,包括换行符 (!) 和单个空格 (!!),这两者都会微妙地影响其行为。这意味着需要付出大量努力才能完成一些甚至不是算法的事情。
说到输入格式,由于 catcodes,它是完全流动的。这个答案非常有趣,就像霍加狓很有趣。所以即使你认为你知道 TeX 的语法,你(可能)只知道普通 TeX 格式的语法。或者 LaTeX 的语法。或者 ConTeXt 的语法。
TeX 是一种宏语言。这意味着通过设计,其代码为自我修改,与该
goto
声明就程序结构损坏而言,原因已由 Dijkstra 在该文章中给出。然而!TeX 是不是宏语言,因为它的指令最终会扩展到排版原语。TeXbook 中突出地宣传了这一事实,将 TeX 的操作模式描述为“眼睛”、“嘴巴”和“胃”;嘴巴就是宏语言。前面提到的输入格式问题是“眼睛”;排版部分是“胃”。当然,除了当它是不是只是排版,例如
\def
指令的执行。如果您阅读此网站,您会偶尔听到人们谈论“完全可扩展”;他们指的是仅在口中处理的代码(作为宏)。它是不太为人所知为什么会有这种区别,但正如 TeX 的许多方面一样,这可能只是时代的产物。在其他条件相同的情况下,这并不是学习编程语言的好广告。
TeX 实际上没有变量、循环或条件之类的编程结构。它确实有寄存器和
\loop...\repeat
结构(通过巧妙操纵自修改代码来工作),并且确实有固定数量的内置\if
,但一般来说,你会发现自己在编写一个临时的、非正式指定的、充满错误的 Common Lisp 一半实现(或者,因为我实际上不懂 Lisp,也许我应该说“一半 C”)。除了坏习惯之外,不要以为你通过这种方式不会学到任何东西。除非你已经知道你试图发明的结构,否则你最终会得到意大利面条式的代码。尽管 TeX 构成编程语言的部分从定义上来说就是纯粹可扩展的部分,但现有的结构大多不可扩展。一些其中是可扩展导致非常模糊的策略,例如在这个问题及其答案。让程序员担心
if
语句的代码分支实际上会干扰分支代码本身可能不太合理。TeX 没有函数的概念,这是少数几个至关重要的和函数式语言都同意这一点。它的宏确实吸收了“参数”,但由于宏语言固有的棘手扩展问题,这些参数并不总是代码中写的内容。即使它们是写的是什么,因为棘手引起争论的问题他们是仍然并不总是如你所想的那样。
奇怪的是,尽管括号之类的东西对于 TeX 的功能至关重要,尽管它是一种图灵完备的语言,而且尽管输入的确切格式对于其功能至关重要,但没有本地方法可以轻松完成诸如确定(当然是可扩展的)下一个字符是否是括号之类的事情!请参阅 Bruno Le Floch 对这个答案。这实际上是一个很好的教训,说明了“图灵完备”和“无所不能”之间的区别,所以从这个意义上说,这是一个人编程教育中很有价值的一部分。
它没有调试功能。它确实提供了跟踪代码的能力,但这无法精细控制,并且并不总是能揭示关键事实,例如“所有这些变量现在具有什么值?”。假设您可以说出变量是什么,因为它们可能就像“函数”一样是宏。
从本质上讲,使用 TeX 编程就是一场无休止的挫折之旅,因为代码格式、多轮解析和后续(可能是部分)执行以及定义不明确的程序状态等细节而令人沮丧。尽管“只有 perl 可以解析 Perl”,至少对于 Perl 来说确实如此,因为它提供了极其丰富的编程风格和功能(当然,包括修改其自身语法的能力)。在 TeX 中,您可能很难让它执行加法。
答案2
这更像是一个长评论而不是答案。
TeX 是学习编程的极好资源。它告诉你如何不写代码!编程的基本原则之一是,你应该编写易于他人阅读的代码。我发现很多 TeX 代码,尤其是 TeX 早期编写的 TeX 宏代码,都难以阅读。部分原因是 TeX(宏语言)的特性。但更大的原因是宏作者使用的可怕的宏命名约定,尤其是@
在变量名中间使用。
例如,考虑宏\overset
和\underset
,它们是包的一部分amsmath
。它们被定义为
\newcommand{\overset}[2]{\binrel@{#2}%
\binrel@@{\mathop{\kern\z@#2}\limits^{#1}}}
\newcommand{\underset}[2]{\binrel@{#2}%
\binrel@@{\mathop{\kern\z@#2}\limits_{#1}}}
其中辅助宏\binrel@
和\binrel@@
定义在amsbsy.sty
:
\def\binrel@#1{\begingroup
\setboxz@h{\thinmuskip0mu
\medmuskip\m@ne mu\thickmuskip\@ne mu
\setbox\tw@\hbox{$#1\m@th$}\kern-\wd\tw@
${}#1{}\m@th$}%
\edef\@tempa{\endgroup\let\noexpand\binrel@@
\ifdim\wdz@<\z@ \mathbin
\else\ifdim\wdz@>\z@ \mathrel
\else \relax\fi\fi}%
\@tempa
}
\let\binrel@@\relax
如果将这两个宏命名\checkmathbinrel
为\usemathbinrel
。然后,人们可以阅读代码来理解意图宏。
笔记:我无意批评编写代码的作者。我知道编写代码时,计算效率比可读性更重要。
现代 TeX 代码,无论是以 LaTeX3 样式还是以 ConTeXt 样式编写,都更易于阅读。例如,这是在 ConTeXt 中定义相同宏的方式:
\def\math_binrel_apply#1%
{\begingroup
\setbox\scratchbox\hbox
{\thinmuskip 0mu
\medmuskip -1mu
\thickmuskip -1mu
\setbox\scratchbox\hbox{$#1\mathsurround\zeropoint$}%
\kern-\wd\scratchbox
${}#1{}\mathsurround\zeropoint$}%
\ifdim\wd\scratchbox<\zeropoint
\endgroup
\expandafter\mathbin
\else\ifdim\wd\scratchbox>\zeropoint
\endgroup
\expandafter\expandafter\expandafter\mathrel
\else
\endgroup
\expandafter\expandafter\expandafter\firstofoneargument
\fi\fi}
\unexpanded\def\overset#1#2%
{\math_binrel_apply{#2}{\mathop{\kern\zeropoint#2}\limits\normalsuperscript{#1}}}
\unexpanded\def\underset#1#2%
{\math_binrel_apply{#2}{\mathop{\kern\zeropoint#2}\limits\normalsubscript {#1}}}
请注意,正确的缩进和富有表现力的变量名使得代码更容易理解(也许会以效率为代价)。
因此,如果你读过很多 TeX 代码,你就会遇到用不同风格编写的相同宏。你可以比较这两种实现,以了解如何编写可读性强的代码。
1:这个\edef\@tempa{\endgroup ...} \@tempa
技巧,或者说这个{\begingroup ... \engroup \expandafter ... \fi}
诀窍是 TeX 编程的标准部分。)
答案3
(这是另一条与 Aditya 的回答类似的长评论。)
是的, 它能帮助你理解其他语言(从理论意义上来说)。
以及 Ryan 给出的所有理由。
在数学中,我们总是在寻找反例、异常值、稍微奇怪但仍符合定义的东西。事实上,我们有一整本书的标题都是“数学中的反例”X“这些书是人们书架上的“必读”书籍之一。我们认为,要真正理解一个概念,你必须把它推向极端,并研究那里发生了什么——仅仅理解激发这个概念的安全、舒适的例子是不够的。所以我们学习空集、离散度量空间、非豪斯多夫流形、不可分离的 Banach 空间、具有一个元素的域(又名 F_un)以及许多类似的东西。我们这样做并不是因为我们真的想研究它们,而是因为如果我们真的想理解“S^3 是光滑流形”这句话的含义,我们还需要了解那些奇怪的例子。
如果你只想学习一门编程语言,并且从来不好奇为什么如果它的工作方式是那样的,那么就不要费心去研究 TeX。把它当作魔法,忘掉它。但如果你真的想了解编程是什么,并学习它能做什么真的那么你真的应该看看 TeX,因为没有什么能与它媲美。
我首先要从 TeX 的用途角度来看待它(我只是从这里推断,我对它的起源并不了解)。它是一种嵌入文档排版语言的编程语言,而后者是主要部分。因此,编程语言大多数时候都必须隐藏自己,这也是空格和换行符比其他语言更重要的原因之一:它们在文档排版部分很重要。
TeX 的另一个主要独特功能是,它的大多数用户都不是程序员,也永远不会想成为程序员。因此,它的设计再次考虑到了这一点。作者希望\emph{hello}
“按照说明书上说的做”,\alpha
意思是“我想要一个字母字符”,因此扩展(而不是函数调用)更接近于普通用户期望。
(但我不是专家,绝对不是程序员。所以也许我的观点受到了程序开发的影响。)
答案4
2004 年,Victor Eijkhout 教授了一门基于 TeX 作为编程语言的计算机科学课程。今年,他发布了课程笔记(见http://www.lulu.com/product/paperback/the-computer-science-of-tex-and-latex/18798879)。我最近买了这本书,准备为 TUGboat 写一篇评论。这本书给我的第一印象是,TeX 作为一种语言确实能让你深入了解计算机科学和编程的许多方面。