魔法书《SICP》的简明介绍 - 为什么要学习SICP

写在前面

斜体 表示你需要对斜体部分的含义作出细细的斟酌和思考

加粗字 表示这部分内容是被强调的,需要注意

目的

本文的目的主要在于解答以下问题:

  • 为什么要学习SICP?
  • 学习SICP的作用?
  • SICP讲了什么?

SICP和编程的关系(为什么要学习SICP)

元知识和知识

想要理解学习SICP和学习编程有什么样的关系?也许这样一个类比是有益理解的——学习SICP之于编程就像学习 学习 本身。让我们再具体点,当我们在讨论学习数学、学习物理时,我们的研究对象是数学和物理,当我们在讨论学习学习的时候,我们的研究对象不是某一具体的科目,而是 学习 本身。在其他地方,你也许见过 元知识 这样的词语,其含义表示是 关于知识的知识,和上文讲的学习 学习 本身其实是一个意思。

那么:

  • 元知识知识(一般性知识) 的差别在哪里?
  • 为什么要区分这两个概念?
  • 学习元知识有什么好处?

解答:

相比于元知识,一般性知识更加专注于某一个领域,这使得一般性知识具有了某种特殊性(specialized),而元知识的关注点则更加彰显了其通用性。之所以区分这两个概念,是因为这有助于我们解答以下这个问题——是否存在一种高效且适用于任何领域的学习方法? 或者说 是否存在这样一种知识,能够使我们更好的学习、研究其他知识。显然,这个问题的描述就是学习元知识的好处。

举个例子:

科学方法 就是一种元知识,拿物理学和经济学来说,这两门都算科学,但它们研究的对象显然都不一样,物理学更加符合我们对 科学 这个单词的认知,那么经济学为什么也算科学呢?因为它采用科学方法来研究经济。注意尽管物理学和经济学的研究对象不一样,甚至可以说大不相同(一般性知识),但是却都可以用科学方法(元知识)来研究它们。

回到正文

在讲述了知识和元知识的关系后,让我们回到SICP和编程的关系中来。

学习SICP并不会教会你如何写出一个博客、论坛程序(一般性知识)。SICP的目的在于教会你,当你掌握了不论是写一个博客、还是一个论坛程序的一般性知识后,如何写出一个 的程序。

以上是从更加笼统的角度谈述SICP这本书籍目的,接下来会再具体一点。

本书的作者认为计算机科学和计算机的关系没有紧密到需要在名字中带上计算机三个字,比起说是一门科学,则更像一门艺术,并指出其真正的研究目标在于解决 如何形式化解决问题的过程?

为了理解这个目标,我们先来看看什么是 过程 ?

这里我们需要先区分两种知识:

  • 叙述性知识
  • 指令性知识

前者告诉你是什么,后者告诉你 具体怎么去做,而 过程就是指令性知识

所以,再通俗的解释下,作者认为的计算机科学的目在于解决,(在计算机中)如何以一种精确、规范性的方法来表述指令性知识

在尝试达到计算机科学的这一目标时,我们会遇到一些问题,其中最主要和重要的问题就是 当我们尝试构建非常巨大的系统时,我们如何确信这些系统是正确没有错误的 换句话说 为什么我们可以构建出非常巨大的系统,且又保证它们的正确性,这是因为存在着 控制大型系统复杂度的技术

控制大型系统复杂度的技术 就是本书要 实际 讨论的主题,本书的目的也在于教会读者掌握这些技术,所以当你疑惑SICP这本书到底再讲什么的时候,回想下这个这本书的讨论的主题,就很容易得出相应的答案了。虽说本书的主要目的在于教授控制复杂度的技术,但是本书同样教授了为掌握这些技术需要学习的基础知识,而这些基础知识恰巧也是相关领域的核心部分,这也是为什么SICP被推荐作为编程入门书的原因(不是第一本学习编程的书,而是第一本入门编程的书)。

控制大型系统复杂度的技术

相比较电气学和物理学来说,计算机科学处理的是一种理想化的组件,我们的想法和实现之间没有差距,为了理解这句话,我们来看一个例子。一个物理学家10秒钟可以撕开1张纸(这个不难),请问物理学家如何在5秒中撕开1张纸?答案很简单,只要物理学家的撕纸速度加倍就行了。那么,假设要让物理学家在1秒中撕开10张纸呢?答案依旧很简单,只要物理学家的速度变为原来的10倍就行,但我们都知道现实中是不可能的。不可能的原因就是我们理解 对于计算机科学,我们的想法和实现之间没有差距 的关键。因此,当我们尝试构建一个大型系统时,我们不会受到物理世界的约束,不需要考虑现实世界的误差、额外影响,唯一限制我们的是我们自身大脑的思维能力

三种主要技术

黑盒抽象

我们先来理解 黑盒抽象 的含义:黑盒抽象是一种有关 使用一样事物而无需了解其内部实现 的思想。

如果细细思考下来,不仅于计算机科学,我们生活的世界其实就构建在这种思想之上。我们坐飞机不需要考虑飞机的实现原理,我们买车票而不必在乎是什么样的魔力使得我们可以通过一张小纸条就坐上一个呜呜叫的运输工具去往诗和远方。

让我们回到计算机科学中来,来尝试思考下 为什么我们需要黑盒抽象?

上文刚才讲述了,当我们在构建大型系统时,我们受到的唯一限制就是我们大脑的思维能力。黑盒抽象可以使我们从那些实现细节中解放出来,从而有更多的精力来关注我们想要关注的事物——构建大型系统本身。

其次,也有助于在大型系统中更换组件,我们依赖黑盒的功能来组织系统,而非黑盒的实现细节,对于实现同样功能的黑盒来说,我们不会因为其中一个内部实现用for语句,一个用了while语句,就无法互换这两个盒子。

对于黑盒抽象来说,思考以下问题是有益的:

  • 用什么来构建这些盒子
  • 如何组合这些盒子
  • 如何抽象出两个盒子中的公共部分,构建出新的盒子,并且作为其他盒子的支柱
  • 如何找出和抽象盒子中共通的部分

举例

让我们看看一些黑盒的例子:

  • 编程语言中的函数
  • Java中的类
  • 任何语言的模块机制
  • React、Vue框架

让我们以一个简单的有关 如何找出和抽象盒子中共同的部分 的例子来结束黑盒抽象这部分内容

1
2
3
4
5
6
7
8
9
10
const cube = x => x * x * x;
const square = x => x * x;

// cube和square是两个盒子,且都是对元素按次数乘以自身思想的特例
// 现在我们来表述元素按次数乘以自身思想的通用形式,并将cube和square构建其之上

const power = (x, n) => n === 0 ? 1 : x * power(x, n - 1);

const cube = x => power(x, 3);
const square = x => power(x, 2);

这是个简单的例子,一眼就可以看出共通的部分,问题在于程序的规模变大时,其共同的部分就无法一眼看出了,而SICP则讲述了如何在规模庞大的系统上找出和抽象共通的部分。

接口约定

通用操作

我们来考虑这样一个问题:对于一个通用加法系统(不仅可以对数字相加,还能对相加字符串,相加多项式),如何在不影响其他相加功能的基础上,增加新的相加功能?

其解决方法就是通过约定接口,了解过JAVA的人可能知道JAVA里有接口的存在,然而要注意的是JAVA的接口只是接口约定思想的一种实现,只要符合接口约定的思想,任何语言都可以构建自己的接口机制。

JavaScript在ES6中也提供了接口,任何一个实现了[Symbol.iterator]方法的对象,就可以作为let of的对象,详见点此

大型结构和模块化

何对复杂的现实世界建模并构造大型程序,有两种方法:

  • 面向对象编程
  • 函数式编程

定义新的语言(语言抽象)

当纯粹的设计已经无法控制系统复杂度时,一门新的语言也许是一个好的选择(就像Vue和React)。

这里谈的新语言不是写一个python或者js出来,设计一门新语言的意图是为了强调某一个方面,并且这里谈论的新语言并非想象中的那么“重”,举个例子,以前端来说,Vue和React可以说就是两门新的语言,它们构建于JS之上,可以使用JS的语法,并且强调了某些细节(数据映射成视图,自动操作DOM结构),忽略了某些细节(手动操作DOM结构,人脑同步各种状态)。其中Vue也许比React更像一门新的语言(前者多了一些DSL,后者的JSX只是语法糖),但其中的思想是一样的。

如何掌握所有的编程语言

虽然讲述编程语言不是SICP的主要目标,但是本书还是在这方面提供了不少有益的帮助

SICP提供了一种 通用的编程语言模型,即任何一门语言都要关注以下三点:

  • 基本元素
  • 组合的方法:如何组合基本元素构成复合的元素
  • 抽象的方法:
    • 如何把基本元素组合,封成 黑盒
    • 如何使用这些盒子
    • 如何标识和找到这些盒子
    • 如何把这些盒子同样当作基本元素进行组合和抽象

因为教授如何掌握编程语言不是SICP的主要目标,尽管提供了相应的帮助,但没有详细的发展,王垠的博客里详细的扩充了这一段内容,见此