




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
Go指南Hello,世界Go编程语言指南。该指南被分为三个部分:基础概念、方法和接口,以及并发。在指南后有若干个练习需要读者完成。该指南可以进行交互。点击“运行”按钮(或按Shift+Enter)可以在远程服务器上编译并执行程序。结果展示在代码的下面。这些例子程序展示了Go的各个方面。在指南中的程序可以成为你积累经验的开始。编辑程序并且再次执行它。当你准备好继续了,点击“向后”按钮或按PageDown键。Go本地化该指南也有其他语言版本:BrazilianPortuguese—PortuguêsdoBrasilCatalan—CatalàHebrew—עִבְרִיתJapanese—日本語Romanian-RomânăChinese—普通话点击“向后”按钮或者按PageDown继续。包每个Go程序都是由包组成的。程序运行的入口是包`main`。这个程序使用并导入了包"fmt"和`"math"`。按照惯例,包名与导入路径的最后一个目录一致。导入这个代码用圆括号组合了导入,这是“factored”导入语句。同样可以编写多个导入语句,例如:import"fmt"import"math"导出名在导入了一个包之后,就可以用其导出的名称来调用它。在Go中,首字母大写的名称是被导出的。Foo和FOO都是被导出的名称。名称foo是不会被导出的。执行代码。然后将math.pi改名为math.Pi再试着执行一下。函数函数可以没有参数或接受多个参数。在这个例子中,`add`接受两个int类型的参数。注意类型在变量名_之后_。(参考这篇关于Go语法定义的文章了解类型以这种形式出现的原因。)函数(续)当两个或多个连续的在这个例子中,xint,yint函数命名参数是同一类型,则除了最后一个类型之外,其他都可以省略。被缩写为x,yint多值返回函数可以返回任意数量的返回值。这个函数返回了两个字符串。命名返回值函数接受参数。在Go中,函数可以返回多个“结果参数”,而不仅仅是一个值。它们可以像变量那样命名和使用。如果命名了返回值参数,一个没有参数的return语句,会将当前的值作为返回值返回。变量var语句定义了一个变量的列表;跟函数的参数列表一样,类型在后面。初始化变量变量定义可以包含初始值,每个变量对应一个。如果初始化是使用表达式,则可以省略类型;变量从初始值中获得类型。明变量在函数中,`:=`简洁赋值语句在明确类型的地方,可以用于替代var定义。(`:=`结构不能使用在函数外,函数外的每个语法块都必须以关键字开始。)基本类型Go的基本类型有Basictypesboolstringintint8int16int32int64uintuint8uint16uint32uint64uintptrbyte//uint8的别名rune//int32的别名//代表一个Unicode码float32float64complex64complex128常量常量的定义与变量类似,只不过使用const关键字。常量可以是字符、字符串、布尔或数字类型的值。数值常量数值常量是高精度的_值_。一个未指定类型的常量由上下文来决定其类型。也尝试一下输出needInt(Big)吧。forGo只有一基本的for循环除了没有而`{}`是必须的。种循环结构——`for`循环。了`()`之外(甚至强制不能使用它们),看起来跟C或者Java中做的一样,for(续)跟C或者Java中一样,可以让前置、后置语句为空。for是Go的“while”基于此可以省略分号:C的while在Go中叫做`for`。死循环如果省略了循环条件,循环就不会结束,因此可以用更简洁地形式表达死循环。ifif语句除了没有了`()`之外(甚至强制不能使用它们),看起来跟C或者Java中的一样,而`{}`是必须的。(耳熟吗?)if的便捷语句跟for一样,`if`语句可以在条件之前执行一个简单的语句。作用域仅在if范围之内。(在最后的return语句v看看。)由这个语句定义的变量的处使用if和else在if的便捷语句定义的变量同样可以在任何对应的else块中使用。练习:循环和函数作为练习函数和循环的简单途径,用牛顿法实现开方函数。在这个例子中,牛顿法是通过选择一个初始点z然后重复这一过程求Sqrt(x)的近似值:为了做到这个,只需要重复计算10次,。然后,修循改环条件,使得当值停止改变(或循环。观察迭代次数是否变化。结果与[[/pkg/math/#Sqrt][math.Sqrt]接近并且观察不同的值(1,2,3,……)是如何逐步逼近结果的变改非常小)的时候退出吗?提示:定义并初始化一个浮点值,向其提供一个浮点语法或使用转换:z:=float64(1)z:=1.0结构体一个结构体(`struct`)就是一个字段的集合。(而type的含义跟其字面意思相符。)结构体字段结构体字段使用点号来访问。指针Go有指针,但是没有指针运算。结构体字段可以通过结构体指针来访问。通过指针间接的访问是透明的。结构体文法结构体文法表示通过结构体字段的值作为列表来新分配一个结构体。使用Name:语法可以仅列出部分字段。(字段名的顺序无关。)特殊的前缀&构造了指向结构体文法的指针。new函数表达式new(T)分配了一个零初始化的T值,并返回指向它的指针。vart*T=new(T)或t:=new(T)sliceslice指向一个数组,并且包含了长度信息。[]T是一个元素类型为T的slice。对slice切片slice可以重新切片,创建一个新的slice值指向相同的数组。表达式s[lo:hi]表示从lo到hi-1的slice元素,含两端。因此s[lo:lo]是空的,而s[lo:lo+1]有一个元素。构造sliceslice由函数make创建。这会分配一个零长度的数组并且返回一个slice指向这个数组:a:=make([]int,5)//len(a)=5为了指定容量,可传递第三个参数到`make`:b:=make([]int,0,5)//len(b)=0,cap(b)=5b=b[:cap(b)]//len(b)=5,cap(b)=5b=b[1:]//len(b)=4,cap(b)=4空sliceslice的零值是`nil`。一个nil的slice的长度和容量是0。(了解更多关于slice的内容,参阅文章[[/doc/articles/slices_usage_and_internals.html][slice:使用和内幕]]。)rangefor循环的range格式可以对slice或者map进行迭代循环。range(续)可以将值赋值给_来忽略序号和值。如果只需要索引值,去掉“,value”的部分即可。练习:slice实现`Pic`。它返回一个slice的长度`dy`,和slice中每个元素的长度的8位无符号整数`dx`。当执行这个程序,到的函数包括>x^y,(x+y)/2和`x*y`。循环来分配[][]uint8中的每个`[]uint8`。)uint8(intValue)在类型之间进行转换。)它会将整数转换为灰度(好吧,蓝度)图片进行展示。图片的实现已经完成。可能用(需要使用(使用mapmap映射键到值。map在使用之前必须用make而不是new来创建;值为nil的map是空的,并且不能赋值。map的文法map的文法跟结构体文法相似,不过必须有键名。map的文法(续)如果顶级的类型只有类型名的话,可以在文法的元素中省略键名。修改map在mapm中插入或修改一个元素:m[key]=elem获得元素:elem=m[key]删除元素:delete(m,key)通过双赋值检测某个键存在:elem,ok=m[key]如果key在m中,`ok`为true。否则,ok为`false`,并且elem是map的元素类型的零值。同样的,当从map中读取某个不存在的键时,结果是map的元素类型的零值。练习:map实现`WordCount`。它应当返回一个含有s中每个“词”个数的map。函数wc.Test针对这个函数执行一个测试用例,并打印成功或者失败。你会发现strings.Fields很有帮助。函数为值函数也是值。Functionvalues函数的闭包并且函数是完全闭包的。函数adder返回一个闭包。每个闭包被绑定到了特定的sum变量上。练习:斐波纳契闭包现在来通过函数做些有趣的事情。实现一个fibonacci函数,返回一个函数(一个闭包)可以返回连续的斐波纳契数。switch你可能已经猜到switch可能的形式了。除非使用fallthrough语句作为结尾,否则case部分会自动终止。switch的执行顺序switch的条件从上到下的执行,当匹成配功的时候停止。(例如,switchi{case0:casef():}当i==0时不会调用`f`。)注意:Goplayground中的时间总是从2009-11-1023:00:00UTC开始,如何校验这个值作为一个练习留给读者完成。没有条件的switch没有条件的switch同`switchtrue`一样。这一构造使得可以用更清晰的形式来编写长的if-then-else链。进阶练习:复数立方根让我们通过complex64和complex128来探索一下Go内建的复数。对于立方根,牛顿法需要大量循环:找到2的立方根,确保算法能够工作。在math/cmplx包中有Pow函数。方法和接口方法Go没有类。然而,仍然可以在结构体类型上定义方法。方法接收者出现在func关键字和方法名之间的参数中。方法(续)事实上,可以对包中的任意类型定义任意方法,而不仅仅是针对结构体。不能对来自其他包的类型或基础类型定义方法。接收者为指针的方法方法可以与命名类型或命名类型的指针关联。刚刚看到的两个Abs方法。一个是在*Vertex指针类型上,而另一个在MyFloat值类型上。有两个原因需要使用指针接收者。首先避免在每个方法调用中拷贝值(如果值类型是大的结构体的话会更有效率)。其次,方法可以修改接收者指向的值。尝试修改Abs的定义,同时Scale方法使用Vertex代替*Vertex作为接收者。当v是Vertex的时候Scale方法没有任何作用。`Scale`修改`v`。当v是一个值(非指是Vertex的副本,并且无法修改原始值。Abs的工作方式是一样的。只不过,仅仅读取`v`。所以读取的是原始值(通过指针)还是针),方法看到的那个值的副本并没有关系。接口接口类型是由一组方法定义的集合。接口类型的值可以存放实现这些方法的任何值。隐式接口类型通过实现那些方法来实现接口。没有显式声明的必要。隐式接口解藕了实现接口的包和定义接口的包:互不依赖。因此,也就无需在每一个实现上增加新的接口名称,这样同时也鼓励了明确的接口定义。包io定义了Reader和`Writer`;其实不一定要这么做。错误错误是可以用字符串描述自己的和方法`Error`,返回字符串:typeerrorinterface{Error()string任何东西。主要思路是由预定义的内建接口类型`error`,}当用fmt包的多种不同的打印函数输出一个error时,会自动的调用该方法。练习:错误从之前的练习中复制Sqrt函数,并修改使其返回error值。Sqrt接收到一个负数时,应当返回一个非nil的错误值。复数同样也不被支持。创建一个新类型typeErrNegativeSqrtfloat64为其实现func(eErrNegativeSqrt)Error()string使其成为一个`error`,该方法就可以让ErrNegativeSqrt(-2).Error()返回`"cannotSqrtnegativenumber:-2"`。注意:在Error方法内调用fmt.Print(e)将会让程序陷入死循环。可以通过先转换免这个问题:`fmt.Print(float64(e))`。请思考这是为什么呢?修改Sqrt函数,使其接受一个负数时,返回ErrNegativeSqrt值。e来避Web服务器包http通过任何实现了http.Handler的值来响应HTTP请求:packagehttptypeHandlerinterface{ServeHTTP(wResponseWriter,r*Request)}在这个例子中,类型Hello实现了`http.Handler`。访问http://localhost:4000/会看到来自程序的问候。注意:这个例子无法在基于web的指南用户界面运行。为了尝试编写web服务器,可能需要[[/doc/install/][安装Go]]。练习:HTTP处理实现下面的类型,并在其上定义ServeHTTP方法。在web服务器中注册它们来处理指定的路径。typeStringstringtypeStructstruct{GreetingstringPunctWhostringstring}例如,可以使用如下方式注册处理方法:http.Handle("/string",String("I'mafrayedknot."))http.Handle("/struct",&Struct{"Hello",":","Gophers!"})图片Packageimage定义了Image接口:packageimagetypeImageinterface{ColorModel()color.ModelBounds()RectangleAt(x,yint)color.Color}文档了解全部信息。)同样,`color.Color`和color.Model也是接口,但是通常因为直接使用预定义的实现image.RGBAColor和image.RGBAColorModel而被忽视了。练习:图片还记得之前编写的图片生成器吗?现在来另外编写一个,不过这次将会返回image.Image来代替slice的数据。的Image类型,要实现[[/pkg/image/#Image][必要的方法`pic.ShowImage`。自定义]],并且调用Bounds应当返回一个`image.Rectangle`,例如`image.Rect(0,0,w,h)`。ColorModel应当返回`image.RGBAColorModel`。At应当返回一个颜色;在这个例子里,在最后一个图片生成器的值v匹配`color.RGBAColor{v,v,255,255}`。练习:Rot13读取器一般的模式是io.Reader包裹另一个`io.Reader`,用gzip.NewReader函数输入一个`io.Reader`(gzip的数据流)并且返回一个同样实现了io.Reader的`*gzip.Reader`(解io.Reader的`rot13Reader`,用ROT13修改数据流中的所有的字母进行密某些途径修改特定的流。压缩后的数据流)。实现一个实现了文替换。rot13Reader已经提供。通过实现其Read方法使得它匹配`io.Reader`。并发goroutinegoroutine是由Go运行时环境管理的轻量级线程。gof(x,y,z)开启一个新的goroutine执行f(x,y,z)f,x,y和z是当前goroutine中定义的goroutine在相同的地址空间中运行,因此访问共享内存必须进行同步。`[[/pkg/sync/][sync]]`提供了这种可能,不过在Go中并不经常用到,因为,但是在新的goroutine中运行`f`。有其他的办法。(在接下来的内容中会涉及到。)channelchannel是有类型的管道,可以用channel操作符<-对其发送或者接收值。ch<-v//将v送入channelch。v:=<-ch//从ch接收,并且赋值给v。(“箭头”就是数据流的方向。)和map与slice一样,channel使用前必须创建:ch:=make(chanint)默认情况下,在另一端准备好之前,发送和接收都会阻塞。这使得goroutine可以在没有明的确锁或竞态变量的情况下进行同步。缓冲channelchannel可以是_带缓冲的_。为make提供第二个参数作为缓冲长度来初始化一个缓冲channel:ch:=make(chanint,100)向缓冲channel发送数据的时候,只有在缓冲区满的时候才会阻塞。当缓冲区清空的时候接受阻塞。修改例子使得缓冲区被填满,然后看看会发生什么。range和close发送者可以close一个channel来表示再没有值会被发送了。接收者可以通过赋值语句的第二参数来测试channel是否被关闭:当没有值可以接收并且channel已经被关闭,那么经过v,ok:=<-ch之后ok会被设置为`false`。循环`fori:=rangec`会不断从channel接收值,直到它被关闭。注意:只有发送者才能关闭channel,而不是接收者。向一个已经关闭的channel发送数据会引起panic。还要注意:channel与文件不同;通常情况下无需关闭它们。只有在需要告诉接收者没有更多的数据的时候才有必要进行关闭,例如中断一个`range`。selectselect语句使得一个goroutine在多个通讯操作上等待。select会阻塞,直到条件分支中的某个可以继续执行,这时就会执行那个条件分支。当多个都准备好的时候,会随机选择一个。默认选择当select中的其他条件分支都没有准备好的时候,`default`分支会被执行。为了非阻塞的发送或者接收,可使用default分支:select{casei:=<-c://使用idefault://从c读取会阻塞}练习:等价二叉树可以用多种不同的二叉树的叶子节点存储相同的数列值。例如,这里有两个二叉树保存了序列1,1,2,3,5,8,13。用于检查两个二叉树是否存储了相同的序列的函数在多数语言中都是相当复杂的。这里将使用Go的并发和channel
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
评论
0/150
提交评论