代码之髓笔记

不同的编程语言之间相差很大,在熟悉了一门之后再去学另一门时总会感到别扭、生涩。C里面的指针,在Java里面就被隐藏了起来。在C、Java中必须显式声明类型的变量,在Python中却能进行动态地变化,更不要说Python中map等的函数,直接打开了函数式编程的大门。但不同的语言之间,都有类似的部分,如变量、函数的声明,if、for、while语句等等。

那么不局限在一门具体的语言,站在一定的高度看其间的异同和发展,就是《代码之髓:编程语言核心概念》要做的事。作者在前言中就指出,在学习中需要做到以下三点:

  • 在比较中学习
  • 在历史中学习
  • 在实践中学习

具体到编程语言,第一条指通过比较多种语言,总结出某种语言的独有特点,以及多种语言的共有特点。
第二条是指通过追溯语言的发展历史,了解语言是如何产生、变化和消失的,探寻语言发展演变的轨迹。
第三条是亲自进行程序设计。边实践边思考如何编程,才能深入理解语言设计者的意图,同时也能发现自己原先理解不到位之处。

书第1、2章分别讲学习方法和语言的诞生历史,从第3章开始介绍编程语言的相关概念。下面摘录自己认为重要的内容以及相关笔记。

书摘

语法

语法就是程序设计者规定的解释程序编写方式的一系列规则。
编程语言的描述一般可以分为语法及语义。语法是说明编程语言中,哪些符号或文字的组合方式是正确的,语义则是对于编程的解释。(来源:维基百科

补充:如何自己创建一种编程语言

函数

函数的调用是以栈的形式来实现的,具体的实现可见UnderStanding the Stack和CSAPP的相关章节。

错误处理

错误处理的方法大体可分为两种,使用返回值和异常处理。前者以C语言为代表,如今的大多数语言都支持后者。异常的传递使得其追踪变得困难,Java使用了检查型异常来避免漏查抛出异常的可能,但这种机制并没有得到普及。

名字和作用域

早期的语言中,整个程序共用一个对照表,即变量名字的有效范围是整个程序。由于全局变量每次被改写都会波及整个程序,我们就得小心翼翼地避免变量名不小心被重复使用。但随着程序规模的扩大,防止名字冲突变得苦难,于是产生了使用作用域的概念。作用域又分动态作用域和静态作用域。动态作用域即把变量原来的值事先保存在函数入口处,在出口处写回变量中。但多个函数仍一张对照表。而静态作用域按函数区分对照表。

类型

对于计算机来说,如果仅仅给定一串比特列,它是不知道这应该解释为整数还是浮点数。因此,需要有表示这个值为何种类型的额外的信息,这就是类型。

通过将不同类型进行组合得到复杂的类型后,使用中会出现想更改其中一部分却又不想全部重新定义的再利用需求。因此出现了构成要素部分可变的类型,即总称型。想要表现不同的情况时,出现了以类型为参数创建类型的函数。C++语言中的模板、Java语言中的泛型以及Haskell语言中的类型构造器可以说就是这种创建类型的机制。

把类型的信息和数值看做整体的方式叫动态类型,反之则为静态类型。

静态类型语言在编译时确定类型,同时编译时也检查了类型的一致性。这一点动态类型语言是无法做到的。
既不放弃编译时的类型检查,也想尽量减少麻烦的类型声明,要实现这一要求就要用到计算机自动推论确定类型的方法。

容器

万能的容器是不存在的。根据容器的使用目的、使用方式和操作类型的不同,最适宜的容器类型也会相应地变化。

补充:字符编码常识及问题解析

并发

交替的两种方式:

  • 协作式多任务模式——在合适的节点自发进行交替
  • 抢占式多任务模式——一定时间后进行交替

并行执行的两个处理之间出现竞态条件必须同时满足以下三个条件:

  • 两个处理共享变量
  • 至少一个处理会对变量进行修改
  • 一个处理未完成之前另一个处理有可能介入进来

反之,只要三个条件中有一个不具备,就可以编写适合于并行处理的安全的程序。

  • 没有共享:进程和actor模型(通过不共享内存而是传递消息的方式来在并行处理时进行信息交互)
  • 不修改:const、val、Immutable
  • 不介入:
    • 线程的协调——fibre、coroutine、green thread
      处理介入的原因是抢占式线程,那么使用协调模式的线程就可以解决了。
    • 表示不便介入的标志——锁、mutex、semaphore

锁的问题:

  • 死锁——上锁的顺序
  • 无法组合——借用事务内存来解决

对象和类

归集变量与函数建立模型的方法

  • 模块
  • 函数和变量放在
  • 闭包

像这种不受限制,可以赋值给变量,也可以作为函数的参数传递,又可以作为函数的返回值返回的值被称为first class的值。

闭包:一个包含了自由变量的开放表达式,它和该自由变量的约束环境组合在一起后,实现了一种封闭的状态。

类的三大作用:

  • 实例生成器
  • 可行操作的功能说明
  • 代码再利用的单元

继承与代码再利用

继承的实现策略大体可以分为三种

  • 一般化和专门化
  • 共享部分的提取
  • 差异实现

使用多重继承时该如何解决名字解释的问题?

  1. 禁止多重继承 使用委托 接口
  2. 按顺序进行搜索 深度搜索->c3线性化确定顺序 (父类不必子类先被检查 如果是从多个类中继承下来则优先检查先书写的类)
  3. 混入式处理 定义仅包含所需功能的类并把它与需要添加这些功能的更大的类糅合在一起
  4. Trait 当类用于创建实例时,作为再利用单元来说就显得太大了。Trait即是把再利用单元的作用特别化,设定一些更小的结构(特性=方法的组合)。