本文所参考的资料包含
Packages, variables and functions
包
fmt
表示format
的缩写,其中包含了格式化输入输出相关的函数在函数中,如果连续的变量均为同类型,那么可以只定义最后一个变量的类型
What is the difference between := and = in Go?
:=
前者将会包含有变量创建的部分,如果左边的变量已经存在,那么直接赋值,否则先创建再赋值=
只能完成赋值部分,不包含变量的创建部分同时,
var
单独完成变量的创建任务在package scope中,每条语句必须由关键字开头,如
var
,func
等,也就是说,在这里无法使用short variable declaration
Go中的基本类型包括
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15bool
string
int int8 int16 int32 int64
uint uint8 uint16 uint32 uint64 uintptr
byte // alias for uint8
rune // alias for int32
// represents a Unicode code point
float32 float64
complex64 complex128每一个变量在被声明之后,默认为
zero value
,并且不同类型的zero value
不同0
for numeric types,false
for the boolean type, and""
(the empty string) for strings.
Go是强类型语言,不支持隐式类型转换
Flow control statement: for, if, else, switch and defer
- for loop
- 不同于C, Java中的for循环,此处不需要添加括号
- Go中只存在这一种循环结构
- 后面必须添加
{}
对循环语句进行包围,不可以省略
- defer 关键字,通过压栈的方法将相关语句顺序压入栈中,当同级的语句执行完毕之后,按照栈顺序执行defer 的语句
More types: structs, slices, and maps
slicing是一种数据结构,并且每次切片,不是创建了一个新的变量,只是对原有数组的遍历方式发生了改变
同时,可以对slicing literal进行多次切片,每次切片都是对最原始的数组的切片
len(s)
获得切片的当前长度cap(s)
获得切片的最大长度空切片的
zero value
是nil
这里特别区分一下len方法以及cap方法
前者表示当前切片对应的元素个数,仅仅与slicing时给定的上边界与下边界有关
后者更偏向于描述了原数组的信息,表示根据当前的切片起点,遍历到原数组的最后一个元素,在切片数组中的下标
Methods and interfaces
Go中不含有类,但是可以针对结构体定义方法
语法中,只需要在func 以及 方法名之间,指定
receiver argument
a method is just a function with a receiver argument.
关于
receiver
存在两种类型,分别是指针类型以及值类型,很明显,前者可以直接对传入的参数进行修改,而后者不可以With a value receiver, the
Scale
method operates on a copy of the originalVertex
value. (This is the same behavior as for any other function argument.) TheScale
method must have a pointer receiver to change theVertex
value declared in themain
function.对于
receiver
类型为指针的方法,通过非指针类型对象调用方法是可以的,Go将会自动将v.Scale(10)
转化为&v.Scale(10)
;但是对于接受参数类型为指针的函数而言,必须传入指定类型的变量。对于
receiver
为值类型的方法,每次调用会有一次copy,将当前结构体中的数据copy入一个新建的结构体对象,之后在该对象上调用方法,因此如果多次使用值传递的方法,那么程序的性能不会很好接口定义了一些通用的方法
在Go中,可以定义接口类型的变量,该变量可以指向任何实现了接口中定义的全有方法的结构体
需要注意的是,由于方法的
receiver
含有值传递、指针传递两种,因此如果结构体中的方法只定义了指针类型的,那么对接口进行赋值时,也只能赋值指针类型的结构体通过assertion方法,可以将接口中存储的对象提取出来
注意assertion方法有两个返回值,第一个用于存储对应变量,第二个返回是否成功
eg.
f, ok := i.(float64)
一个空接口可以用来表示任意类型的结构体或者自带类型的变量
通过
type switches
可以方便地判断接口变量中存储的变量类型,并且根据不同的情况定义不同的行为1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20package main
import "fmt"
func do(i interface{}) {
switch v := i.(type) {
case int:
fmt.Printf("Twice %v is %v\n", v, v*2)
case string:
fmt.Printf("%q is %v bytes long\n", v, len(v))
default:
fmt.Printf("I don't know about type %T!\n", v)
}
}
func main() {
do(21)
do("hello")
do(true)
}1
2
3Twice 21 is 42
"hello" is 5 bytes long
I don't know about type bool!特殊接口 —
Stringer
1
2
3type Stringer interface {
String() string
}该接口类似于Python中的
__str__(self)
方法,在直接print变量的时候,将会执行该方法,便于进行调试特殊接口 —
error
1
2
3type error interface {
Error() string
}每次函数调用,尽力那个返回两个值,第一个为正确执行返回的值,第二个为程序的执行结果是否合法
特殊接口 —
io.Reader
,其中包含有一个方法func (T) Read(b []byte) (n int, err error)
Read
populates the given byte slice with data and returns the number of bytes populated and an error value. It returns anio.EOF
error when the stream ends.可以认为这是一个输入流,传入字节类型的数组,方法返回之后,n表示其中已经被填写的字节数
Concurrency
通过关键字
go
来开启协程特性 —
select
通过该关键字,可以很轻松地实现,多个协程在执行完毕之后,才可以执行的协程,定义这样的拓扑关系;在多个同级别协程中,随机地选择一个可以执行的协程 eg.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28package main
import "fmt"
func fibonacci(c, quit chan int) {
x, y := 0, 1
for {
select {
case c <- x:
x, y = y, x+y
case <-quit:
fmt.Println("quit")
return
}
}
}
func main() {
c := make(chan int)
quit := make(chan int)
go func() {
for i := 0; i < 10; i++ {
fmt.Println(<-c)
}
quit <- 0
}()
fibonacci(c, quit)
}给定一棵二叉树,编写一个遍历其中的所有结点,并且将结点信息输入通道的函数
注意 这里在启动协程的时候,妥善处理了发送者主动关闭通道的行为
只有这样,后面在取元素的时候,第二个标志位
ok
才有效1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45package main
import "golang.org/x/tour/tree"
import "fmt"
/*
type Tree struct {
Left *Tree
Value int
Right *Tree
}
*/
// Walk walks the tree t sending all values
// from the tree to the channel ch.
func Walk(t *tree.Tree, ch chan int) {
if t == nil {
return
}
ch <- t.Value
if t.Left != nil {
Walk(t.Left, ch)
}
if t.Right != nil {
Walk(t.Right, ch)
}
}
func main() {
ch := make(chan int)
t := tree.New(1)
go func() {
Walk(t, ch)
close(ch)
}()
for {
val, ok := <- ch
if ok == false {
break
} else {
fmt.Println(val)
}
}
}
How to write Go code
Go文件的存储主要分为两个部分,分别是
bin
文件夹用于存储编译好的可执行文件,在配置好环境变量之后,如果存在这样的一个可执行文件
bin/hello
那么在命令行中只需要键入
hello
即可运行该文件src
文件夹源代码文件,主要包含有三部分: 包文件(package source)、命令文件(command source)、测试文件
test source
通过
go install
命令进行编译,生成可执行文件到bin
文件夹需要注意的是,存在所谓的
base path
,也就是说,在install的时候,要么从src
文件夹中执行该命令;要么直接进入最底层的源代码文件夹,直接go install
base path
也适用于引用其他包对于每一个源代码文件,在名称后面加上
xx_test.go
,并且在其中编写测试用例,就可以完成测试功能执行命令为
go test
,使用方法同go install