2022-07-23  2022-07-23    4793 字  10 分钟
> 部分摘录,不成体系,中间会有不少的碎碎念 🥳,如果其中的一两句话引起了你的共鸣,绝对是原作者的功劳。如果,你感兴趣,强烈推荐阅读原作。

“我认为,在计算机科学中保持计算中的趣味性是特别重要的事情。这一科学在起步时饱含着趣味性。当然,那些付钱的客户们时常觉得受了骗。一段时间之后,我们开始严肃地看待他们的抱怨。我们开始感觉到,自己真的像是要负起成功地、无差错地、完美地使用这些机器的责任。我不认为我们可以做到这些。我认为我们的责任是拓展这一领域,寻求新的发展方向,并在自己的家里保持趣味性。我希望计算机科学领域不要丧失趣味意识。最重要的是,我希望我们不要照本宣科,你所知道的有关计算的东西,其他人也都能学到。绝不要认为似乎成功计算的钥匙就掌握在你的手里。你所掌握的,也是我认为并希望的,就是智慧:那种看到这一机器比你第一次站在它面前时所能做得更多的能力,这样你才能将它向前推进。”

– Alan J. Perilis (1922.4.1 - 1990.1.7)

解决大规模问题需要经过一系列规划,其中的大部分东西只有在工作进程中才能做出来,这些规划中充满着与手头问题的特殊性相关的情况。如果想要把做地规划这件事情作为一种智力活动来欣赏,你就必须转到计算机的程序设计(programming),你需要读或者写计算机程序 – 而且要大量地做。有关这些程序具体是关于什么的、服务于哪类应用等通常并不重要,重要的是它们的性能如何,在用于构造更大的程序时能否与其他程序平滑衔接。程序员必须同时追求具体部分的完美和集成的适宜性。

在本书里使用“程序设计”一词时,所关注的是程序的创建、执行和研究,这些程序是用一种 Lisp 方言书写的,以便在数字计算机上执行。采用 Lisp 并没有对我们可以编程的范围施以任何约束或者限制,只不过确定了程序描述的记法形式。

重要的是思想!

本书中要讨论的各种问题都牵涉三类需要关注的对象:人的大脑、计算机程序的集合以及计算机本身。

每一个计算机程序都是现实中或者精神中的某个过程的一个模型,通过人的头脑孵化出来。这些过程出现在人们的经验或者思维之中,数量上数不胜数,详情琐碎繁杂,任何时候人们都只能部分地理解它们。我们很少能通过自己的程序将这种过程模拟到永远令人满意的程度。正因为如此,即使我们写出的程序是一个经过仔细雕琢地离散符号集,是一组交织在一起的函数,也需要不断地演化:当我们对于模型的认识更深入、更扩大、更广泛时,就需要修改程序,直至这一模型最终到达了一种亚稳定状态。而在这时,程序中就又会出现另一个需要我们去为之奋斗的模型。

计算机程序设计领域之令人兴奋的源泉,就在于它所引起的连绵不绝的发现,在我们的头脑之中,在由程序所表达的计算机制之中,甚至在由此所导致的认识爆炸之中。如果说艺术解释了我们的梦想,那么计算机就是以程序的名义执行着它们。

就其本身的所有能力而言,计算机是一位一丝不苟的“工匠”:它的程序必须正确,我们所希望的所有东西,都必须表述得准确到每一点细节。就像在其他所有使用符号的活动中一样,我们需要通过论证使自己相信程序的真。

可以为 Lisp 本身赋予一个语义(可以说是另一个模型),比如说,一个程序的功能可以在谓词演算 ❔ 里描述,那么就可以用逻辑方法做出一个可接受的正确性论证。不幸的是,随着程序变得更大、更复杂(实际上它们几乎总是如此),这种描述本身的适宜性、一致性和正确性也都变得大家非常值得怀疑了。因此,很少能够看到有关大程序正确性的完全形式化的论证。因为大程序是从小东西成长起来的,所以开发出一个标准化的程序结构的武器库,并保证其中每种结构的正确性 – 我们称之为惯用法,再学会如何利用一些已经证明很有价值的组织技术,将这些结构组合成更大的结构,都是至关重要的。

理解这些技术,对于参与被称为程序设计的具有创造性的事业是最本质的。 特别值得提出的是,发现并掌握强有力的组织技术,将提升我们构造大型的重要程序的能力。反过来说,由于写大程序非常耗时费力,这也推动着我们去发明新方法,以减轻由于大程序的功能和细节而带来的沉重负担。

比如,赫赫有名的 Spring Framework 💯

与程序不同,计算机必须遵守物理定律。在任何情况下,硬件都是在比我们编程时所需要关心的层次更低的层次上的操作的。

将 Lisp 程序变换到“机器”程序的过程本身也是抽象模型,是通过程序设计做出来的。研究和构造它们,能使人更加深刻地理解与任何模型的程序设计有关的程序组织问题。当然,计算机本身也可以这样模拟。

区分出上述三类需要关注的对象,并不仅仅是为了策略上的便利。即使有人说这种逻辑区分不过是人头脑里的东西,它也加速了这些关注对象之间的转换,它们的丰富性、活力和潜力,只能通过现实生活中的不断演化去超越。我们至多只能说,这些关注对象之间的关系是基本稳定的。计算机永远也不够大也不够快。硬件技术的每一次突破都带来了更大规模的程序设计事业、新的组织原理,以及更加丰富的抽象模型。

每个读者都应该反复地问自己:“到哪里才是尽头?” – 但是不要问得过于频繁,以免忽略了程序设计的乐趣,使自己陷入一种喜忧参半的呆滞状态中。

在我们写出的程序里,有些程序执行了某个精确的数学函数(但是约不够精确),例如排序,或者找出一系列数中的最大元,确定素数性,找出平方根。我们将这种程序称为算法,关于它们的最佳行为已经有了许多认识,特别是关于两个重要的参数:执行的时间和对数据存储的需求。 程序员应该追求好的算法和惯用法。即使某些程序难以精确地描述,程序员也有责任去估计它们的性能,并要继续设法去改进之。

这里有一段关于 Scheme 和 Pascal 的论述,也很精彩,附上一观。

很难找到这样的两种语言,它们清晰地代表着围绕这两种语言而聚集起来的两种差异巨大的变化。Pascal 是为了建立金字塔 – 壮丽辉煌、令人震撼,是由各就各位的沉重巨大筑起的静态结构。而 Lisp 则是为了构造有机体 – 同样壮丽辉煌并令人震撼,是由各就各位但却永不静止的无数简单的有机体片段构成的动态结构。在两种语言里都采用了同样的组织原则,除了其中特别重要的一点不同之外:赋予 Lisp 程序员个人可用的自由支配权,要远远超过在 Psacal 社团里可找到的东西。Lisp 程序大大抬高了函数库的地位,使其可用性超越了催生它们的那些具体应用。作为 Lisp 的内在数据结构,表对于这种可用性的提升起着最重要的作用。表的简单结构和自然可用性反映到函数里,就使它们具有了一种奇异的普适性。而在 Pascal 里,数据结构的过度声明导致函数的专用性。采用 100 个函数在 1 个数据结构上操作,远远优于用 10 个函数在 10 个数据结构上操作。这带来的必然后果是,金字塔矗立在那里千年不变,而有机体则必须深化,否则就会死亡。

正如人工智能的目标可以预见到的,其研究产生出许多重要的程序设计问题。在其他程序文化中,源源不断的问题孵化出一种又一种新的语言。确实,在任何非常大的程序设计工作中,一条有用的组织原则就是通过发明新语言去控制和隔离作业模块之间的信息流动。 这些语言越来越不基础,逐渐逼近系统的边界,逼近我们人类最经常与之交互的地方。结果是系统里包含着大量重复的、复杂的语言处理功能。

Lisp 有着非常简单的语法和语义,程序的语法分析可以看作一种很简单的工作。这样,语法分析技术对于 Lisp 程序几乎就没有价值,语言处理器的构造不会成为大型 Lisp 系统发展和变化的阻碍。最后,正是这种语法和语义的极端简单性,给所有 Lisp 程序员带来了负担和自由。任何规模的 Lisp 程序,除了那种寥寥几千的程序外,都包含着考虑周到的各种功能。发明并调整,调整恰当后再去发明!让我们举起杯,祝福那些将思想镶嵌在重重括号之间的 Lisp 程序员。

第 1 版前言

“为什么说程序设计很容易成为一种媒介,用于表述理解浮浅、草率而就的思想。” – Marvin Minsky

我们所设计的这门计算机入门课程主要考虑了两个方面。首先,我们希望建立起一种认识:计算机语言并不仅仅是一种让计算机去执行操作的方式,更重要的,它是一种表述有关方法学的思想的新颖的形式化媒介。因此,程序必须写得能够代价阅读,偶尔地去供计算机执行。其次,我们相信,在这一层次的课程里,最基本的材料并不是特定程序设计语言的语法,不是高效完成某种功能的巧妙算法,也不是算法的数学分析或者计算机的本质基础,而是一些能够控制大型软件系统的复杂性的技术

我们的目标是,完成了这一科目的学生能对程序设计的风格要素有一种很好的审美观。他们应该掌握了控制大型系统的复杂性的主要技术。他们应该能够去读 50 页长的程序,只要该程序是以一种值得模仿的形式写出来的。他们应该知道在什么时候哪些东西不需要去读,哪些东西不需要去理解。他们应该很有把握地去修改一个程序,同时又能保持原来作者的精神和风格。

这些技能并不仅仅适用于计算机程序设计。我们所教授和提炼出来的这些技术,对于所有的工程设计都是能通用的。我们在适当的时候隐藏起一些细节,通过创建抽象去控制复杂性。我们通过建立约定的界面,以一种“混合与匹配”的方式组合起一些标准的、已经很好理解的片段去控制复杂性。我们通过建立 一些新的语言去描述各种设计,每种语言强调设计中的一个特定方面并降低其他方面的重要性,以控制复杂性。

设计这门课程的基础是我们的一种信念 – “计算机科学”并不是一种科学,而且其重要性也与计算机本身并无太大关系。计算机革命是关于我们如何思考,以及我如何去表达自己的思考的。在这个变化里,最基本的东西就是出现了一种或许最好称为过程性认识论的现象 – 如何从命令式的观点去研究知识的结构,这一观点与经典数学领域中所采用的更具说明性的观点是完全不同的。数学为精确处理“是什么”提供了一种框架,而计算则为精确处理“怎样做”提供了一种框架。

过程性认识论 ❓

在教授这时的材料时,我们采用的是 Lisp 语言的一种方言。我们绝没有形式化地教授这一语言,因为完全不必那样做。我们只是使用它,学生可以在几天之内就学会它。这也是类 Lisp 语言的重要优点:只有为数不多地几种构造复合表达式的方式,几乎没有语法结构。所有的形式化性质都可以在一个小时里讲完,就像下象棋的规则一样。在很短时间之后,我们就可以不再去管语言的语法细节(因为这里根本就没有),而进入真正的问题 – 弄清楚我们需要计算什么,怎样将问题分解为一组可以控制的部分,如何对各个部分开展工作。 Lisp 的另一个优势在于,与我们所知的任何其他语言相比,它可以支持(但不是强制性的)更多以模块化的方式分解程序的大规模策略。我们可以做过程性抽象和数据抽象,可能通过高阶函数抓住公共的使用模式,可以用赋值和数据操作去模拟局部状态,可以利用流和延时求值连接起一个程序里的各个部分,可以很容易地实现嵌入性语言。所有这些都融合在一个交互式的环境里,带有对递增式程序设计、构造、测试和排除错误的绝佳支持功能。我们要感谢一代又一代的 Lisp 大量,从 John McCarthy 开始,是他们打造了这样一个优美的、具有空前威力的好工具。

好的吧,光是看序言,已经开始一脸懵比了……

作为我们所用的 Lisp 方言,Scheme 试图将 Lisp 和 Algol 的威力和优雅集成到一起。我们从 Lisp 那里取来了元语言的威力 – 简单的语法形式、程序与数据对象的统一表示,以及带有废料收集(垃圾回收)的堆分配数据。我们从 Algol 那里取来了词法作用域和块结构,这是来自当年参加 Algol 委员会的礼物。🎉

第 2 版前言

软件可能确实与其他任何东西都不同,它的本意就是被抛弃:这一观点就是总将它看作一个肥皂泡吗? – Alan J. Perlis

我们重新设计了本书里大部分主要程序设计系统,包括通用型算术系统、解释器、寄存器机器模拟器和编译器,也重写了所有的程序实例,以保证任何符合 IEEE 的 Scheme 标准(IEEE 1990)的 Scheme 实现都能运行这些代码。

这一版本中强调了几个新问题,其中最重要的是计算模型时对于时间的处理所起的中心作用:带有状态的对象、并发程序设计、函数式程序设计、随性求值和非确定性程序设计。这里为并发和非确定性新增加了几节,我们也设法将这一论题集成到整本书里,贯穿始终。

结语

真心话,这些经典书籍的写作者,都是一群激情的、有趣的、可爱的人。致敬。