当前位置: 首页 > 科技观察

Go编程语言简介

时间:2023-03-16 00:48:13 科技观察

(以下是我硕士论文的节选,几乎是整个2.1章节,有CS背景的人快速入门Go)Go是一门并发编程的命令式编程语言,它主要由其创建者Google开发,最初主要由RobertGriesemer、RobPike和KenThompson开发。该语言的设计始于2007年,2009年发布了初始版本;第一个稳定版本是2012年发布的1.0版。1Go具有C风格的语法(无预处理器)、垃圾收集,并且类似于贝尔实验室开发的前辈:Newsqueak(RobPike)、Alef(PhilWinterbottom)和Inferno(Pike、Ritchie等人),内置并发支持,使用所谓的Gogoroutines和通道(基于Hoare的“通信顺序进程”理论的协程)。2Go程序以包的形式组织。包本质上是一个包含Go文件的文件夹。包中的所有文件共享相同的命名空间,包中的符号有两种可见性:以大写字母开头的符号对其他包可见,而其他符号是包私有的:funcPublicFunction(){fmt.Println("Helloworld")}funcprivateFunction(){fmt.Println("Hellopackage")}typesGo有一个相当简单的类型系统:没有子类型(但类型转换),没有泛型,没有更多的状态函数,只有一些基本的类型:基本类型:int、int64、int8、uint、float32、float64等structinterface:方法的集合map[K,V]:从键类型到值类型的映射[number]Type:元素数组oftypeType[]Type:某种类型的切片(指向具有长度和函数的数组的指针)chanType:线程安全的队列指针*T指向其他类型的函数命名类型:可能有其他类型的别名关联方法(LCTT译注:这里的别名不是指Go1.9中的新特性“typealiases”):typeTstruct{fooint}typeT*TtypeTOtherNamedType命名类型与它们的底层类型完全不同,所以你不能将它们赋值给彼此,但是一些操作符,比如+,与相同底层的命名类型对象一起工作数字类型(因此您可以在上面的示例中添加两个T)。映射、切片和通道是类似引用的类型——它们实际上是包含指针的结构。其他类型,包括数组(具有固定长度并且可以复制),按值传递(复制)。类型转换类型转换类似于C或其他语言中的类型转换。它们是这样写的:TypeName(value)constGo有“无类型”字面量和常量。1//untypedintegerliteralconstfoo=1//untypedintegerconstantconstfooint=1//int类型常量untypedvalues可以分为以下几类:UntypedBool,UntypedInt,UntypedRune,UntypedFloat,UntypedComplex,UntypedStringandUntypedNil(Go称它们为原始类型,其他原始类型可用于具体类型,如uint8)。可以将无类型值分配给从基础类型派生的命名类型;eg:typesomeTypeintconstuntyped=2//UntypedIntconstbarsomeType=untyped//OK:untypedcanbeassignedtosomeTypeconsttypedint=2//intconstbar2someType=typed//error:intcannotbeassignedtosomeType接口和对象As上面提到,接口是方法的集合。Go本身不是一种面向对象的语言,但它支持在命名类型上关联方法:当声明一个函数时,你可以提供一个接收者。接收者是函数的一个额外参数,可以在函数之前传递并参与函数查找,像这样:typeSomeTypestruct{...}typeSomeTypestruct{...}func(s*SomeType)MyMethod(){}funcmain(){varsSomeTypes.MyMethod()}如果一个对象实现了所有的方法,它就实现了一个接口;例如,*SomeType(注意指针)实现了下面的接口MyMethoder,所以*SomeType类型的值都可以作为MyMethoder类型的值。最基本的接口类型是interface{},它是一个带有空方法集的接口——任何对象都满足该接口。typeMyMethoderinterface{MyMethod()}合法的接收者类型有些限制;例如,命名类型可能是指针类型(例如,类型MyIntPointer*int),但这种类型不是合法的接收者类型。控制流Go提供了三个主要的控制语句:if、switch和for。这些语句与其他C风格语言中的语句非常相似,有一些区别:条件语句没有括号,因此条件语句是ifa==b{}而不是if(a==b){}。需要大括号。所有的语句都可以初始化,比如thisifresult,err:=someFunction();err==nil{//使用结果}switch语句可以使用分支中的任何表达式switch语句可以处理空表达式(等于true)默认情况下,Go不会从一个分支转到下一个(不需要break语句),在块的末尾使用fallthrough将转到下一个分支。循环语句for不仅可以循环取值范围:forkey,val:=rangemap{dosomething}Go协程关键字go会生成一个新的Go协程goroutine,这是一个可以并行执行的函数。它可以用于任何函数调用,甚至是匿名函数:funcmain(){...gofunc(){...}()gosome_function(some_argument)}用于通信顺序进程的扩展。channel是并发安全的队列,可以选择是否缓冲数据:varunbuffered=make(chanint)//直到数据读取完成,数据块完成varbuffered=make(chanint,5)//最多有5个未读块运算符<-用于与单个通道通信。valueReadFromChannel:=<-channelotherChannel<-valueToSend语句select允许多个通道通信:select{caseincoming:=<-inboundChannel://一条新消息caseoutgoingChannel<-outgoing://消息可以发送}defer语句Go提供了语句defer允许在函数退出时调用预定函数。可以用于资源释放操作,例如:funcmyFunc(someFileio.ReadCloser){defersomeFile.close()/*文件相关操作*/}当然,它允许匿名函数作为被调用函数,编写调用函数可以像往常一样使用任何变量。错误处理Go不提供异常类或结构化错误处理。但是,它通过使用第二个和后续返回值返回错误来处理错误:funcRead(p[]byte)(nint,errerror)//built-intype:typeerrorinterface{Error()string}mustCheckforerrorsincodeorassignto_:n0,_:=Read(Buffer)//忽略错误n,err:=Read(buffer)iferr!=nil{returnerr}有两个函数可以快速跳出和恢复调用堆栈:恐慌()和恢复()。当panic()被调用时,调用堆栈开始弹出,每个defer函数都正常运行。当延迟函数调用recover()时,调用堆栈停止弹出并返回函数panic()给出的值。如果我们让调用堆栈正常弹出而不是由于调用panic()函数,recover()将只返回nil。在下面的示例中,defer函数将捕获任何由panic()抛出的错误类型的值,并将其存储在错误返回值中。这种方法有时用于第三方库,如解析器,以增强递归代码的可读性,同时保持公共函数仍然使用正常的错误返回值。funcFunction()(errerror){deferfunc(){s:=recover()switchs:=s.(type){//typeswitchcaseerror:err=s//s现在有类型错误default:panic(s)}}}数组和切片前面说过,数组是值类型,切片是指向数组的指针。切片可以从现有的数组切片创建,或使用make()创建,这会创建一个匿名数组来保存元素。slice1:=make([]int,2,5)//分配5个元素,其中2个被初始化为0slice2:=array[:]//整个数组的切片slice3:=array[1:]//除了第一个对元素进行切片之外,还有更多可行的切片操作组合,但需要清晰直观。使用append()函数可以将切片用作可变长度数组。slice=append(slice,value1,value2)slice=append(slice,arrayOrSlice...)切片也可以用作函数的可变长度参数。地图地图是简单的键值存储容器,支持索引和赋值。但它们不是线程安全的。someValue:=someMap[someKey]someValue,ok:=someMap[someKey]//如果key不在someMap中,变量ok将被赋值为`false`someMap[someKey]=someValue