KshZh / blog

my blog

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

浅析编程语言提供的抽象

KshZh opened this issue · comments

commented

大学时读过大名鼎鼎的《SICP》,读了大概前面两到三章,Lisp 语言以及一些习题很多都忘光了,但我至今还记得从中学到的两个字:”抽象“。

我认为,抽象,就是不管具体。它代表一种思考方式,以更高层的角度看待分析问题。它也是一种构建大型复杂系统,降低系统复杂度的方式。抽象很重要,因为普通人脑不是酷睿 I7 处理器,也没有 16GB 内存,精力是有限的。

抽象在计算机世界中无处不在。这里浅析一下编程语言提供的抽象。

实用的编程语言,往往会提供两种抽象能力,过程抽象和数据抽象。

过程抽象

过程抽象,一般是把一段具有某种功能的代码抽象成一个函数,指定它的输入输出,OK,一个具有某种功能的黑盒就制作完成了,给定指定输入,就能得到对应的输出。至于黑盒里面是怎么实现的,包含了什么东西?不知道,我也不需要知道,我所有知道的、只需要知道的就是它的输入和输出。(当然如果遇到 bug 需要排查,或者想要学习一下实现原理另说)

过程抽象是很强大的能力,我们可以制作一个个小黑盒,然后进行组合,制作出更强大的、功能更丰富的黑盒。而使用如此丰富、强大的功能,不需要自己写很多很复杂的代码,只需要调用一下这个黑盒,把数据按照黑盒规定的输入给它就行了。

下面是一个实现求和黑盒的例子。这个例子比较简单,不过想象一下,如果没有过程抽象,一个复杂的功能需要 10000 行代码来实现,当你想用这样一个功能的时候,你就必须在文件里复制粘贴写下这 10000 行代码,再有一个地方要用到,再复制粘贴 10000 行代码。如果有一天,这个功能有 bug 要修复,或者迭代新增小功能,那么如果代码库里有 100 个地方用到了,这 100 个地方全部都要同步修改,改漏了就完了~芭比 q 了~。就算没改漏,这可维护性……

// 一个做平方的黑盒,输入数字 x,输出 x^2。
function square(x) {
    // 对外部来说,黑盒内部的具体实现是怎么样的,不重要,
    // 外部使用者只需要知道黑盒的内部实现满足黑盒提供的输入输出抽象即可。
    // return x*x;
    return Math.pow(x, 2);
}

// 一个输入 x,输出 x 的黑盒。
function identity(x) {
    return x;
}

// 对输入增 1 的黑盒。
function incOne(x) {
    return x+1;
}


function sum(a, b, f) {
    let result = 0;
    for (let i=a; i<=b; i++) {
        result += f(i);
    }
    return result;
}

sum(1, 3, identity); // 6
sum(1, 3, square); // 14
sum(1, 3, x => incOne(square(x))); // 17

image

数据抽象

数据抽象,创建新的数据类型,对现实事物进行抽象建模。

一个数据类型,至少定义了两个方面的内容,一个是它的实例在内存中存储的值,一个是它支持的操作。比如浮点数类型,它在内存中存储了一个 IEEE754 浮点数,支持的基本操作有加减乘除。

下面是一个建模一个二维平面的点的示例。

// 创建一个二维坐标系上的点
const point1 = {
  x: 2,
  y: 3,
  distanceFromOrigin() {
    return Math.sqrt(Math.pow(this.x, 2)+Math.pow(this.y, 2));
  }
};

// 再创建一个……
const point2 = {
  x: 21,
  y: 3.3,
  distanceFromOrigin() {
    return Math.sqrt(Math.pow(this.x, 2)+Math.pow(this.y, 2));
  }
};

// 再创建一个……

可以看到,JS 支持这种简单直接的定义新的数据类型并创建其实例的方式,但是还不够抽象。想象一下要建模一个很复杂的事物,有很多属性,支持很多复杂的操作,创建过程也很复杂,需要对数据做预处理之类的。如果按照这种定义和实例化数据类型的方式,代码很快会变得不可维护。所以 JS 提供了构造器增强我们数据抽象的能力。在其他语言中,可能是类。当然,不管是构造器,还是类,都是支持我们实现数据抽象的手段罢了。

function Point(x, y) {
  this.x = x;
  this.y = y;
}
Point.prototype.distanceFromOrigin = function() {
  return Math.sqrt(Math.pow(this.x, 2)+Math.pow(this.y, 2));
};

const point3 = new Point(3, 12);
const point4 = new Point(213, 12);