迭代
对于函数是 first-class 的语言来说,迭代的变形很多,从 Python 的 Comprehensive 到, Perl 的 map, 迭代是最能体会函数式语言威力的地方。
[ x * 5 for x in [1,2,3,4] ]
(map (fn (x) (* x 5)) [1,2,3,4])
map { $_ * 5 } (1 .. 5);
while 守卫循环
while 循环是设置一个条件,内部循环执行一段代码,并且设置迭代变量。
while ($x > 1) {
last if $x == 10;
say($x);
$x++;
}
在 while 条件表达式中的变量是外部的,反复执行代码部分属于内部,但作用于同一个变量。
还有一种 while 守卫循环是在内部赋值:
{
my $x = 0;
$x++;
} until ($x > 10);
其实,只要保护好变量不被意外赋值,两种形式都是可以互相转换的。
并行循环
在支持并行的语言中,如果循环和顺序无关,彼此之间的计算结果没有先后依存关系,就可以使用并行循环。并行循环充分利用多核的计算资源,能明显提高代码的效率。
线程或进程
多个结果不互相依存的计算过程,尤其是网络读写这种耗时较长的工作,通常同时用许多独立的线程来完成,以充分利用等待的时间段,多做些工作。为了搜集和容纳这些进程的工作结果,通常会用到队列。
队列是一种特别的数据结构,有点像数组,但又有时间上的限制,防止死锁进程导致的程序终止。有一些容错机制。
有了多线程的计算模型,代码更像是一个调度,分别监控各个独立的计算模型运行结果,彼此沟通,并对结果进行判断,处理意外。这是一种更高级的编程模型,里面同时容纳多个独立运行的代码,分别管理分配不同的变量表和命名空间。
函数
函数是最广泛应用的一种代码抽象,他把重复运行的代码片段进行命名,抽象出参数和计算过程。
大部分函数返回的参数都是一个, Lua, Golang 可以返回多个,Python 利用元组也能模拟返回多值。函数的自由变量,通常是在定义的位置,获取外部变量的值。为了隔绝这种外部变量,有了闭包的概念。
Javascript 没有块级作用域,所以他的闭包实现是用函数中的函数实现的,这也是一种没有办法的事情。但被人用来炫耀这种技能,这其实是一种错误的语言设计导致的一种变态做法!Python 也是如此,为了增加闭包的功能,增加了 global, nolocal 两个关键字才勉强实现。
Lisp 的函数作用域是动态的,和 Scheme 不同,也就是从 caller 来获取自由变量的值。关于这种设计有很多争论。但我认为,自由变量的存在,本身就是一种错误的设计,由此引发的语言特性的争论,不过是在裂缝上涂泥巴,颜色不同罢了。
命名空间
命名空间让变量,函数的命名更加简单,宽容。C 语言是没有命名空间的。C++ 增加了命名空间,也添加了类。
命名空间彼此之间的变量名称是互相隔离的,即使是在一个命名空间内定义别的命名空间,也是如此。
命名空间从名字上可以有归属关系,但实现并不是嵌套,只是和平行的命名空间有不同的名字继承原则,彼此之间是互相独立的。
不同的语言有不同的命名空间符号导入导出原则。Java 用 public, private 来界定导入导出的权限,而 Golang 则默认使用首字母大写的符号作为 public 符号。
类
类的实质是命名空间,能使用这里定义的函数,必定是由这个类定义的一个数据结构模板生成的。只是调用其中的函数是用另外一种句法:
my $obect = new Class_name
$object->method(args);
因为实际上函数调用,传入的多了一个参数,就是对象本身。类没有名字导出,所有可调用的函数,都是通过对象调用的,只有不能调用的方法,没有可以导出的方法。
类中定义的函数,又叫方法,因为处理的第一个参数,总是固定数据结构的对象,所以有些语言就干脆省略,用 self 代替,由此方法和函数使用不同的关键字。
默认,类和其子类有继承方法的原则,这样一个类可能拥有的方法可能来自很多,这让类设计很容易变得复杂而庞大,因为检索一层层的方法而让面向对象的语言的速度降低。
即使量子计算机和神经元计算机面世,更大的需求出现,计算机能做的事情也变化不大,只是处理的数据多了些,硬件快了些,算法复杂了些罢了。