Spark 是用scala写的,storm是clojure开发的,docker采用了go, 各种编程语言层出不穷,但其实都是马甲,作为一个程序员,我们需要理解那些相通的本质,做到触类旁通.
程序中的基本逻辑控制——跳转,在汇编语言中早就有满足条件后跳转的命令了。所谓跳转,就是告诉机器到那去干活. 就像过去的接线员那样,因而C语言中有个goto语句,它是跳转的最直接的解释.
如果程序中goto多了,恐怕没谁能看懂了. 为了避免goto语句的不便理解,C语言中的if...else语句就变得更让人欢迎了。满足条件时反复执行某区间的代码,goto语句都能做到,而while和break语句所作的事情不是增加了新功能,而是提高了程序的易读性和易写性。for 语句只是让数值渐增的while语句更简洁而已,而foreach语句是为了方便编写对某对象内的所有元素进行某种处理的代码。
函数是为了程序的便于理解和重复使用,把代码的一部分视作有机的整体,然后切分出来并为之命名,这是一种程序设计机制。函数催生了return语句,能够使函数调用后回到程序执行的原来内存地址,使用函数给操作命名的办法来表示操作开始时内存的位置。栈是一个存储有多个值的数据结构,实现后进先出,是管理函数操作的有力工具。了解栈,就可以解释运行时的函数行为了。
递归调用是函数使用的一种方式,是指函数内部再次调用当前函数的过程。当多重嵌套的数据结构无法用for语句实现时,就需要使用递归调用了。
一个程序中尤其在一个函数中,错误处理的传达方法主要有两种:
1)通过函数的返回值来传达出错的信息,调用者通过返回值来判断进行相应的错误处理
2)预先设定好错误处理的代码,错误发生时能跳转到相应的错误处理代码。
方法一存在两个问题,分别是错误遗漏和错误处理导致代码的可读性下降;对于方法二中可追加错误类型和可自主出发出错,这两种功能被现代的异常处理机制所继承。
异常处理逐渐成为了错误处理的主要传达机制. try 是一个为了方便理解的修饰符,触发异常的表述为抛出异常,异常处理在except中,出错也要执行的关键字为finally。而c++采用了资源获取即初始化技术,通过自动调用析构函数来实现类似的机制。一般地,函数调用参数不足时,数组越界等情况,都要抛出异常。Fail First,即异常处理优先, 但是,检查型异常是比较麻烦的.
关于变量,整个程序共用一个参照表,即全局变量的使用范围,作用域是指名字的有效范围。动态作用域是把变量原来的值事先保存在函数入口处,在出口处写回变量中的方式,静态作用域按函数区分各自的对照表,避免全局污染的原因就在于此。一般地,一个程序都会有整体对照表(内置),一张文件级对照表(全局),和一张函数级的对照表(局部)。
千万别轻易认为自己了解了数据类型,类型是个复杂的概念.类型是人们给数据附加的一种追加数据,最初是为了加入数值的类型信息,告诉编译器如何处理,而后,用户自定义类型和面向对象同样是类型,从而产生了作为功能的类型,进而发展为接口,出现了泛型和模板等。把类型的信息和数值看做整体的方式叫做动态类型,在内存上使用了同等类型对待的设计方法,支持类型推断。例如python,都是PyObject。 从内存和何时使用的角度来看待类型会更清晰。
从存储的形式看变量, 存放多个元素的东西可以理解为容器,容器中的数据存在内存中,存储的方式不同容器的体现也不同。数组是顺序存放的,链表同时存放了相对地址,因而对元素多插入频繁的情况,链表更适合。为了简洁地表达计算时间和数据量之间的关系,一般采用O表示法。O(n)是n的数量级,O(1)是常数数量级,树(平衡)中元素的读取时间为O(log n)。
字符串博大精深,是非常重要的元素. 北邮门口的摩尔斯码,到后来各种的字符集编码,程序中多用注释来指明编码方式,unicode带来了统一。一般语言的字符串都带有自身的长度,c中的字符串不知道自身的长度,是最为原始的字符串,NUL即\0是表示字符串终止的特殊字符。Unicode是16bit还是32bit是在编译时通过选项指定的。
并行处理越来越被关注, 单cpu的并行处理实际上是分时交替处理,交替方式有:
1)协作式多任务——在合适的节点交替,基于信任,所有处理都会在适当的间隔后交替处理
2)抢占式多任务——一定时间后交替,不需要终止程序的协助就可以单方面终止它
抢占式多任务存在竞态条件(race condition),或者称为线程安全,竞态条件成立的三个条件:
1)两个处理共享变量
2)至少一个处理会对变量进行修改
3)一个处理未完成前另一个处理会介入进来
只要三个条件有一个不具备,就可以写线程安全的程序了。
规避一,没有共享内存,就不存在竞态条件了,例如利用独立进程和actor模型。
规避二,比如C++中的const,scala中的val,Java中的immutable
规避三,不介入,使用协调模式的线程如coroutine等,也可以使用表示不便介入的标识——锁、mutex、semaphore,实际上是使用中的状态牌。锁的使用问题包括死锁和无法组合,只能寄托于事务内存来奢望解决了。