本文主要是介绍Go,从语言对比分析的角度切入。之因此选择与Python、Erlang对比,是由于作为高级语言,它们语言特性上有较大的类似性,不过最主要的缘由是这几个我比较熟悉。html
Go的不少语言特性借鉴与它的三个祖先:C,Pascal和CSP。Go的语法、数据类型、控制流等继承于C,Go的包、面对对象等思想来源于Pascal分支,而Go最大的语言特点,基于管道通讯的协程并发模型,则借鉴于CSP分支。程序员
如《编程语言与范式》一文所说,无论语言如何层出不穷,全部语言的设计离不开2个基本面:控制流和数据类型。为了提高语言描述能力,语言通常都提供控制抽象和数据抽象。本小节的语言特性对比也从这4个维度入手,详见下图(点击见大图)。golang
图中咱们能够看出,相比于Python的40个特性,Go只有31个,能够说Go在语言设计上是至关克制的。好比,它没有隐式的数值转换,没有构造函数和析构函数,没有运算符重载,没有默认参数,也没有继承,没有泛型,没有异常,没有宏,没有函数修饰,更没有线程局部存储。算法
可是Go的特色也很鲜明,好比,它拥有协程、自动垃圾回收、包管理系统、一等公民的函数、栈空间管理等。编程
Go做为静态类型语言,保证了Go在运行效率、内存用量、类型安全都要强于Python和Erlang。数组
Go的数据类型也更加丰富,除了支持表、字典等复杂的数据结构,还支持指针和接口类型,这是Python和Erlang所没有的。特别是接口类型特别强大,它提供了管理类型系统的手段。而指针类型提供了管理内存的手段,这让Go进入底层软件开发提供了强有力的支持。安全
Go在面对对象的特性支持上作了不少反思和取舍,它没有类、虚函数、继承、泛型等特性。Go语言中面向对象编程的核心是组合和方法(function)。组合很相似于C语言的struct结构体的组合方式,方法相似于Java的接口(Interface),可是使用方法上与对象更加解耦,减小了对对象内部的侵入。Erlang则不支持面对对象编程范式,相比而言,Python对面对对象范式的支持最为全面。数据结构
在函数式编程的特性支持上,Erlang做为函数式语言,支持最为全面。可是基本的函数式语言特性,如lambda、高阶函数、curry等,三种语言都支持。多线程
控制流的特性支持上,三种语言都差很少。Erlang支持尾递归优化,这给它在函数式编程上带来便利。而Go在经过动态扩展协程栈的方式来支持深度递归调用。Python则在深度递归调用上常常被爆栈。闭包
Go和Erlang的并发模型都来源于CSP,可是Erlang是基于actor和消息传递(mailbox)的并发实现,Go是基于goroutine和管道(channel)的并发实现。无论Erlang的actor仍是Go的goroutine,都知足协程的特色:由编程语言实现和调度,切换在用户态完成,建立销毁开销很小。至于Python,其多线程的切换和调度是基于操做系统实现,并且由于GIL的大坑级存在,没法真正作到并行。
并且从笔者的并发编程体验上看,Erlang的函数式编程语法风格和其OTP behavior框架提供的晦涩的回调(callback)使用方法,对大部分的程序员,如C/C++和Java出身的程序员来讲,有必定的入门门槛和挑战。而被称为“互联网时代的C”的Go,其类C的语法和控制流,以及面对对象的编程范式,编程体验则好不少。
全部的语言特性都须要有形式化的表示方式,Go、Python、Erlang三种语言语法的详细对好比下(点击见完整大图第一部分,第二部分,第三部分)。这里(连接)有一个详细的Go 与 C 的语法对比,这也是我没有作Go vs. C对比的一个缘由。
正如Go语言的设计者之一Rob Pike所说,“软件的复杂性是乘法级相关的”。这充分体如今语言关键词(keyword)数量的控制上,Go的关键词是最少的,只有25个,而Erlang是27个,Python是31个。从根本上保证了Go语言的简单易学。
Go语言将数据类型分为四类:基础类型、复合类型、引用类型和接口类型。基础类型包括:整型、浮点型、复数、字符串和布尔型。复合数据类型有数组和结构体。引用类型包括指针、切片、字典、函数、通道。其余数据类型,如原子(atom)、比特(binary)、元组(tuple)、集合(set)、记录(record),Go则没有支持。
Go对C语言的不少语法特性作了改良,正如Rob Pike在《Less is Exponentially More》中提到,Go的“起点: C语言,解决一些明显的瑕疵、删除杂质、增长一些缺乏的特性。”,好比,switch/case的case子程序段默认break跳出,case语句支持数值范围、条件判断语句;全部类型默认初始化为0,没有未初始化变量;把类型放在变量后面的声明语法(连接),使复杂声明更加清晰易懂;没有头文件,文件的编译以包组织,改善封装能力;用空接口(interface {})代替void *,提升类型系统能力等等。
Go对函数,方法,接口作了清晰的区分。与Erlang相似,Go的函数做为第一公民。函数可让咱们将一个语句序列打包为一个单元,而后能够从程序中其它地方屡次调用。函数和方法的区别是指有没有接收器,而不像其余语言那样是指有没有返回值。接口类型具体描述了一系列方法的集合,而空接口interfac{}表示能够接收任意类型。接口的这2中使用方式,用面对对象编程范式来类比的话,能够类比于subtype polymorphism(子类型多态)和ad hoc polymorphism(非参数多态)。
从图中示例能够看出,Go的goroutine就是一个函数,以及在堆上为其分配的一个堆栈。因此其系统开销很小,能够轻松的建立上万个goroutine,而且它们并非被操做系统所调度执行。goroutine只能使用channel来发送给指定的goroutine请求来查询更新变量。这也就是Go的口头禅“不要使用共享数据来通讯,使用通讯来共享数据”。channel支持容量限制和range迭代器。
Go、Python、Erlang三种语言词法符号的详细对好比下(点击见完整大图)。Go的词法符号是3个语言中最多的,有41个,并且符号复用的状况也较多。相对来讲,Python最少,只有31个。
Go语言在词法和代码格式上采起了很强硬的态度。Go语言只有一种控制可见性的手段:大写首字母的标识符会从定义它们的包中被导出,小写字母的则不会。这种限制包内成员的方式一样适用于struct或者一个类型的方法。
在文件命名上,Go也有必定的规范要求,如以_test.go为后缀名的源文件是测试文件,它们是go test测试的一部分;测试文件中以Test为函数名前缀的函数是测试函数,用于测试程序的一些逻辑行为是否正确;以Benchmark为函数名前缀的函数是基准测试函数,它们用于衡量一些函数的性能。
除了关键字,此外,Go还有大约30多个预约义的名字,好比int和true等,主要对应内建的常量、类型和函数。
本小节以TDD方式4次重构开发一个斐波那契算法的方式,来简单展现Go的特性、语法和使用方式,如Go的单元测试技术,并发编程、匿名函数、闭包等。
首先,看一下TDD最终造成的单元测试文件:
package main import ( "testing" ) func TestFib(t *testing.T) { var testdatas = []struct { n int want int64 }{ {0, 0}, {1, 1}, {2, 1}, {3, 2}, {4, 3}, {16, 987}, {32, 2178309}, {45, 1134903170}, } for _, test := range testdatas { n := test.n want := test.want got := fib(n) if got != want { t.Errorf("fib(%d)=%d, want %d\n", n, got, want) } } }
基于递归的实现方案:
func fib1(n int) int64 { if n == 0 || n == 1 { return int64(n) } return fib1(n-1) + fib1(n-2) }
测试结果:
crbsp@fib$ time go test
PASS
ok _/home/crbsp/alex/go/fib 9.705sreal 0m10.045s
user 0m9.968s
sys 0m0.068s
基于goroutine实现的并发方案:
func fib2(n int) int64 { var got int64 var channel = make(chan int64, 2) if n == 0 || n == 1 { return int64(n) } runtime.GOMAXPROCS(2) go func() { channel <- fib1(n - 2) }() go func() { channel <- fib1(n - 1) }() got = <-channel got += <-channel return got }
测试结果:
crbsp@fib$ time go test
PASS
ok _/home/crbsp/alex/go/fib 6.118sreal 0m6.674s
user 0m10.268s
sys 0m0.148s
基于迭代的实现方案:
func fib3(n int) int64 { var a, b int64 a, b = 0, 1 for i := 0; i < n; i++ { a, b = b, a+b } return a }
测试结果:
crbsp@fib$ time go test
PASS
ok _/home/crbsp/alex/go/fib 0.002sreal 0m0.547s
user 0m0.328s
sys 0m0.172s
基于闭包的实现方案:
func fibWrapper4() func() int64 { var a, b int64 a, b = 0, 1 return func() int64 { a, b = b, a+b return a } } func fib4(n int) int64 { var got int64 got = 0 f := fibWrapper4() for i := 0; i < n; i++ { got = f() } return got }
测试结果:
crbsp@fib$ time go test
PASS
ok _/home/crbsp/alex/go/fib 0.002sreal 0m0.411s
user 0m0.260s
sys 0m0.140s
--完--