编程时,你在思考什么 1 - 抽象
初衷
这个系列叫思考编程
。有趣的话题,不是吗?当你使用一种编程语言你在思考什么?如何开始思考这个问题?
更重要的,作为一个程序员,编写代码是我们的工作也是乐趣所在,我们需要思考我们编程的思维过程,这对理解、学习计算机语言有非常大的帮助。
另外,还以一个重要的话题需要思考,就是如何阅读代码,这也是这个系列想要解决的问题的。
抽象
我会从 抽象 开始思考。抽象有很多定义,比如:隐藏细节,暴露接口;有时候 API 会被直接等价成抽象;抽象也有语义,semantic ,的意思,就是这个抽象在运行时的行为是怎么样的。
当然,我也可以从具体的抽象对象思考。一个编程语言最基础的要提供两种抽象:计算资源抽象 和 存储资源抽象。
- 计算机资源包括:CPU 指令,进程,线程,协程。
- 存储资源包括:缓存,内存,硬盘,网络。
这些抽象想我们隐藏了计算机硬件的诸多细节,暴露给我们语义明确的接口,或者API。这就是我们编程的基本模块,编程的时候我们就是用这些模块的组合和互动完成我们的计算任务。
当然这些仅仅是最基础的模块,开发人员还会在此基础上,进一步封装抽象,提供更加高级、但目的性更强的抽象。
举个例子,Python 的数据处理 pandas ,这是一个专门处理 table like 数据的库,它提供了一套 API 可以进行一些数据处理相关的运算,比如 groupby mean sort 等等。而这些 API 隐藏了内部线程和内存配置的细节,因为 Pandas 库内部有独立的Block manager 利用 python 提供的更加底层的没错抽象,管理自己的内存空间。这样,使用者就不必关心内存和CPU 的抽象了,直接可以在 pandas api 层面进行数据处理。
比如当我们初始化一个 DataFrame ,我的脑子里想的是一个表格,他有3列,100行,数据类型都是小数行。
我可能不会去想:我有一片内存的Buffer ,它是线性的,我的stride是3,头部的x字节包含了这个buffe的各种信息,比如列的名字,数据类型等等。不过,当你遇到性能瓶颈的时候,这种次级抽象的理解就可以帮助你解决问题了。
我可能会想:pandas 的内部其实大部分是另一个库的抽象,numpy,比如我的数据其实可以被一个 ndarray 代表,ndarray 就是更加底层的一个库提供的抽象。这样思考有时候会帮助我们更好的使用 pandas。
所以,当我们编程的时候,我们已经不自觉的按照语言和库提供给我们的抽象进行思考了。意识到这一点很重要,因为不同语言提供了迥然不同的抽象。也决定了这个语言可以做哪些事情,不可以做哪些事情。比如 Python 通过一个运行时的垃圾回收器进行内存管理,用户基本不能控制内存的分配跟回收,因此你就不能操作内存上的比特;而 Rust 提供了丰富的内存抽象,你可以控制那些东西分配在栈中,那些分配在堆中,那些是可变的,那些是可变的,那些是不可变的,那些可以被清理,那些不可以等等。
下一期
这一期就到这里,下一期我们具体谈谈不同语言提供的抽象,后面我们谈谈那些代码是写给人看的,那些代码是写给编译器看的等等问题。