如何系统地学习一门编程语言?

「学习一门新的编程语言」是每个程序员都必然会面对的一个课题(注:本文特指工程实践中常用的高级编程语言)。自从高一开始编程到现在,我也接触了不少的编程语言,有的语言已经能在工作中得心应手的使用;有的语言用的不多,但大概了解其设计,也能在官方文档的帮助下简单使用;还有的则之前很了解,但很长时间没有使用过了。

换一份新的工作是最有机会让程序员接触到新的东西的,最近我就应工作需求开始接触一门新的编程语言,学习过程中难免会和已经熟悉的编程语言进行对比,然后发现其实编程语言的学习完全是有「套路」可循的,这篇博客试图总结一下这种「套路」。

总的来说,编程语言各种各样的特性共同决定了这门语言,这样的特性可能大到并发模型的设计,小到某个具体的语法特性。学习一门编程语言的过程就是不断了解这些语言特性并进行实践应用以及工程化使用的过程。系统性地把语言的主要特性明确清楚,可以快速地建立对一门语言的基本认识,并能做到胸有成竹地深入和细化。

下面就一一说明学习一门新的编程语言至少需要明确的语言特性有哪些,供参考。

编译型 or 解释型

如今一门语言是编译型语言还是解释型语言似乎对工程开发的影响越来越小(而且大有相互靠拢之势,界限也变得越来越不清晰),这或多或少得益于各种编辑器和 IDE 对于中间过程的自动化处理。不过,明确一门编程语言是编译型还是解释型依然是基础且非常有必要的,这对于理解一些程序的深层行为逻辑会很有帮助。

其实对于语言设计者来说,编译型和解释型从根本上是语言开发自由度和性能优化能力之间的博弈。

类型系统

基本地,需要了解该语言都原生支持哪些数据类型,而其中哪些是简单/基础类型,哪些又是复杂/复合类型,各个类型之间又有哪些区别等等。

更抽象来看,类型系统又可以从两个维度来定义:

  • 强类型 or 弱类型:核心的区别在于语言是否偏向于容忍隐式的类型转换。一个常见的错误理解是「C/C++ 是强类型语言」,实际上,在 C/C++ 中隐式类型转换很常见,它们是弱类型语言。与之相对的,Python 则是强类型语言。
  • 动态类型 or 静态类型:核心区别在于是否在编译时就能明确每一个变量的类型。对于静态类型来说,如果编译时存在类型错误,是无法编译通过的,而对于动态类型来说,由于程序运行时才能明确类型,所以类型错误通常不会导致编译出错(错误会在运行时暴露出来)。Python 就是动态类型,而 C/C++ 则是静态类型。

模块系统

如今的高级语言基本都会有成熟的模块系统,好的模块系统对于构建大型工程项目来说必不可少,它可以大大提高程序的可复用能力和模块化能力。

了解一门编程语言的模块系统大概至少需要了解这些细节:

  • 依赖的路径是如何解析的?
  • 如何引用自定义模块(文件)?
  • 如何引用第三方的模块或包?

作用域

与模块系统相关的一个很影响实际开发的问题是「变量的作用域(scope)是怎样的」。常见的可以自己问的问题是:

  • 什么情况下是全局作用域?
  • 什么时候又会是函数作用域?
  • 块作用域又是怎样的?

不过,不同的语言对于作用域的定义和分类可能不同,需要具体情况具体了解。

另外,明确语言是「动态作用域」还是「静态(词法)作用域」也非常重要:与动态类型和静态类型的区别类似,静态作用域是在书写代码或者说定义时确定的,而动态作用域是在运行时确定的。静态作用域关注函数在何处声明,而动态作用域关注函数从何处调用,其作用域链是基于运行时的调用栈的。可以看看这个例子做进一步的区分。

支持的编程范式与特色

编程语言支持哪样的编程范式对于工程项目也非常重要,这也基本和语言设计上的一些别具一格不那么常见的特色相关。比如函数式编程中的「函数是一等公民」、面向对象中的「封装、继承和多态」对应到编程语言上应该如何具体实现、面向并行的语言又是如何实现并发的等等。

了解了这些特色你才能更清楚这门语言在工程中更擅长做哪样的事情,更适合哪些项目。

其他

基础的你需要了解基本的语法规则和特性,比如分支、循环、函数等等。更深入一些的时候则可以了解一下语言的架构设计、那些语言特性为什么会这样设计以及它们都是如何被实现的。了解实现不一定非得去看源码,搞清楚语言大体上的实现过程还是比较简单的。

总结

最后,把前文的部分总结成一张思维导图,希望能够让开始学习新编程语言的过程更清晰快速。