Go语言官方指南.docx
- 文档编号:12057065
- 上传时间:2023-04-16
- 格式:DOCX
- 页数:17
- 大小:25.51KB
Go语言官方指南.docx
《Go语言官方指南.docx》由会员分享,可在线阅读,更多相关《Go语言官方指南.docx(17页珍藏版)》请在冰豆网上搜索。
Go语言官方指南
Go指南
Hello,世界
Go编程语言指南。
该指南被分为三个部分:
基础概念、方法和接口,以及并发。
在指南后有若干个练习需要读者完成。
该指南可以进行交互。
点击“运行”按钮(或按Shift+Enter)可以在远程服务器上编译并执行程序。
结果展示在代码的下面。
这些例子程序展示了Go的各个方面。
在指南中的程序可以成为你积累经验的开始。
编辑程序并且再次执行它。
当你准备好继续了,点击“向后”按钮或按PageDown键。
Go本地化
该指南也有其他语言版本:
∙BrazilianPortuguese—PortuguêsdoBrasil
∙Catalan—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的基本类型有Basictypes
bool
string
intint8int16int32int64
uintuint8uint16uint32uint64uintptr
byte//uint8的别名
rune//int32的别名
//代表一个Unicode码
float32float64
complex64complex128
常量
常量的定义与变量类似,只不过使用const关键字。
常量可以是字符、字符串、布尔或数字类型的值。
数值常量
数值常量是高精度的_值_。
一个未指定类型的常量由上下文来决定其类型。
也尝试一下输出needInt(Big)吧。
for
Go只有一种循环结构——`for`循环。
基本的for循环除了没有了`()`之外(甚至强制不能使用它们),看起来跟C或者Java中做的一样,而`{}`是必须的。
for(续)
跟C或者Java中一样,可以让前置、后置语句为空。
for是Go的“while”
基于此可以省略分号:
C的while在Go中叫做`for`。
死循环
如果省略了循环条件,循环就不会结束,因此可以用更简洁地形式表达死循环。
if
if语句除了没有了`()`之外(甚至强制不能使用它们),看起来跟C或者Java中的一样,而`{}`是必须的。
(耳熟吗?
)
if的便捷语句
跟for一样,`if`语句可以在条件之前执行一个简单的语句。
由这个语句定义的变量的作用域仅在if范围之内。
(在最后的return语句处使用v看看。
)
if和else
在if的便捷语句定义的变量同样可以在任何对应的else块中使用。
练习:
循环和函数
作为练习函数和循环的简单途径,用牛顿法实现开方函数。
在这个例子中,牛顿法是通过选择一个初始点z然后重复这一过程求Sqrt(x)的近似值:
为了做到这个,只需要重复计算10次,并且观察不同的值(1,2,3,……)是如何逐步逼近结果的。
然后,修改循环条件,使得当值停止改变(或改变非常小)的时候退出循环。
观察迭代次数是否变化。
结果与[[http:
//golang.org/pkg/math/#Sqrt][math.Sqrt]接近吗?
提示:
定义并初始化一个浮点值,向其提供一个浮点语法或使用转换:
z:
=float64
(1)
z:
=1.0
结构体
一个结构体(`struct`)就是一个字段的集合。
(而type的含义跟其字面意思相符。
)
结构体字段
结构体字段使用点号来访问。
指针
Go有指针,但是没有指针运算。
结构体字段可以通过结构体指针来访问。
通过指针间接的访问是透明的。
结构体文法
结构体文法表示通过结构体字段的值作为列表来新分配一个结构体。
使用Name:
语法可以仅列出部分字段。
(字段名的顺序无关。
)
特殊的前缀&构造了指向结构体文法的指针。
new函数
表达式new(T)分配了一个零初始化的T值,并返回指向它的指针。
vart*T=new(T)
或
t:
=new(T)
slice
slice指向一个数组,并且包含了长度信息。
[]T是一个元素类型为T的slice。
对slice切片
slice可以重新切片,创建一个新的slice值指向相同的数组。
表达式
s[lo:
hi]
表示从lo到hi-1的slice元素,含两端。
因此
s[lo:
lo]
是空的,而
s[lo:
lo+1]
有一个元素。
构造slice
slice由函数make创建。
这会分配一个零长度的数组并且返回一个slice指向这个数组:
a:
=make([]int,5)//len(a)=5
为了指定容量,可传递第三个参数到`make`:
b:
=make([]int,0,5)//len(b)=0,cap(b)=5
b=b[:
cap(b)]//len(b)=5,cap(b)=5
b=b[1:
]//len(b)=4,cap(b)=4
空slice
slice的零值是`nil`。
一个nil的slice的长度和容量是0。
(了解更多关于slice的内容,参阅文章[[http:
//golang.org/doc/articles/slices_usage_and_internals.html][slice:
使用和内幕]]。
)
range
for循环的range格式可以对slice或者map进行迭代循环。
range(续)
可以将值赋值给_来忽略序号和值。
如果只需要索引值,去掉“,value”的部分即可。
练习:
slice
实现`Pic`。
它返回一个slice的长度`dy`,和slice中每个元素的长度的8位无符号整数`dx`。
当执行这个程序,它会将整数转换为灰度(好吧,蓝度)图片进行展示。
图片的实现已经完成。
可能用到的函数包括>x^y,(x+y)/2和`x*y`。
(需要使用循环来分配[][]uint8中的每个`[]uint8`。
)
(使用uint8(intValue)在类型之间进行转换。
)
map
map映射键到值。
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`,和方法`Error`,返回字符串:
typeerrorinterface{
Error()string
}
当用fmt包的多种不同的打印函数输出一个error时,会自动的调用该方法。
练习:
错误
从之前的练习中复制Sqrt函数,并修改使其返回error值。
Sqrt接收到一个负数时,应当返回一个非nil的错误值。
复数同样也不被支持。
创建一个新类型
typeErrNegativeSqrtfloat64
为其实现
func(eErrNegativeSqrt)Error()string
使其成为一个`error`,该方法就可以让ErrNegativeSqrt(-2).Error()返回`"cannotSqrtnegativenumber:
-2"`。
注意:
在Error方法内调用fmt.Print(e)将会让程序陷入死循环。
可以通过先转换e来避免这个问题:
`fmt.Print(float64(e))`。
请思考这是为什么呢?
修改Sqrt函数,使其接受一个负数时,返回ErrNegativeSqrt值。
Web服务器
包http通过任何实现了http.Handler的值来响应HTTP请求:
packagehttp
typeHandlerinterface{
ServeHTTP(wResponseWriter,r*Request)
}
在这个例子中,类型Hello实现了`http.Handler`。
访问http:
//localhost:
4000/会看到来自程序的问候。
注意:
这个例子无法在基于web的指南用户界面运行。
为了尝试编写web服务器,可能需要[[http:
//golang.org/doc/install/][安装Go]]。
练习:
HTTP处理
实现下面的类型,并在其上定义ServeHTTP方法。
在web服务器中注册它们来处理指定的路径。
typeStringstring
typeStructstruct{
Greetingstring
Punctstring
Whostring
}
例如,可以使用如下方式注册处理方法:
http.Handle("/string",String("I'mafrayedknot."))
http.Handle("/struct",&Struct{"Hello",":
","Gophers!
"})
图片
Packageimage定义了Image接口:
packageimage
typeImageinterface{
ColorModel()color.Model
Bounds()Rectangle
At(x,yint)color.Color
}
文档了解全部信息。
)
同样,`color.Color`和color.Model也是接口,但是通常因为直接使用预定义的实现image.RGBAColor和image.RGBAColorModel而被忽视了。
练习:
图片
还记得之前编写的图片生成器吗?
现在来另外编写一个,不过这次将会返回image.Image来代替slice的数据。
自定义的Image类型,要实现[[http:
//golang.org/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`。
并发
goroutine
goroutine是由Go运行时环境管理的轻量级线程。
gof(x,y,z)
开启一个新的goroutine执行
f(x,y,z)
f,x,y和z是当前goroutine中定义的,但是在新的goroutine中运行`f`。
goroutine在相同的地址空间中运行,因此访问共享内存必须进行同步。
`[[http:
//golang.org/pkg/sync/][sync]]`提供了这种可能,不过在Go中并不经常用到,因为有其他的办法。
(在接下来的内容中会涉及到。
)
channel
channel是有类型的管道,可以用channel操作符<-对其发送或者接收值。
ch<-v//将v送入channelch。
v:
=<-ch//从ch接收,并且赋值给v。
(“箭头”就是数据流的方向。
)
和map与slice一样,channel使用前必须创建:
ch:
=make(chanint)
默认情况下,在另一端准备好之前,发送和接收都会阻塞。
这使得goroutine可以在没有明确的锁或竞态变量的情况下进行同步。
缓冲channel
channel可以是_带缓冲的_。
为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`。
select
select语句使得一个goroutine在多个通讯操作上等待。
select会阻塞,直到条件分支中的某个可以继续执行,这时就会执行那个条件分支。
当多个都准备好的时候,会随机选择一个。
默认选择
当select中的其他条件分支都没有准备好的时候,`default`分支会被执行。
为了非阻塞的发送或者接收,可使用default分支:
select{
casei:
=<-c:
//使用i
default:
//从c读取会阻塞
}
练习:
等价二叉树
可以用多种不同的二叉树的叶子节点存储相同的数列值。
例如,这里有两个二叉树保存了序列1,1,2,3,5,8,13。
用于检查两个二叉树是否存储了相同的序列的函数在多数语言中都是相当复杂的。
这里将使用Go的并发和channel来编写一个简单的解法。
这个例子使用了tree包,定义了类型:
typeTreestruct{
Left*Tree
Valueint
Right*Tree
}
练习:
等价二叉树
1.实现Walk函数。
2.测试Walk函数。
函数tree.New(k)构造了一个随机结构的二叉树,保存了值`k`,`2k`,`3k`,...,`10k`。
创建一个新的channelch并且对其进行步进:
goWalk(tree.New
(1),ch)
然后从channel中读取并且打印10个值。
应当是值1,2,3,...,10。
3.用Walk实现Same函数来检测是否t1和t2存储了相同的值。
4.测试Same函数。
`Same(tree.New
(1),tree.New
(1))`应当返回true,而`Same(tree.New
(1),tree.New
(2))`应当返回false。
练习:
Web爬虫
在这个练习中,将会使用Go的并发特性来并行执行web爬虫。
修改Crawl函数来并行的抓取URLs,并且保证不重复。
WheretoGofromhere...
你可以从安装Go开始,或者下载GoAppEngineSDK.
一旦安装了Go,应当继续阅读的内容。
它包含了参考、指南、视频等等更多资料。
了解如何组织Go代码并在其上工作,参阅这个视频,或者阅读[[http:
//golang.org/doc/code.html][如何编写Go代码]]
包手册。
语言本身的帮助,阅读[[http:
//golang.org/ref/spec][语言规范]]是件令人愉快的事情。
进一步探索Go的并发模型,参阅ShareMemorybyCommunicating的代码之旅。
FirstClassFunctionsinGo展示了有趣的函数类型。
GoBlog有着众多的关于Go的文章信息。
mikespook的博客有大量中文的关于Go的文章和翻译。
访问golang.org了解更多内容。
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Go 语言 官方 指南