Go语法笔记

之前阅读A Tour of Go的时候做了些笔记,现在把她整理好发出来。

1. 第一个程序

1
2
3
4
5
import "fmt"

func main() {
fmt.Println("Hello World!")
}

程序运行需要在main包下面,并且从main()函数开始运行package main

导入包可以是分开来写

1
2
import "fmt"
import "math"

也可以是写在一起

1
2
3
4
import (
"fmt"
"math"
)

程序默认是按行来分割,如果不按行分割的话要用分号隔开import ("fmt" ; "math")

导入的包可以重命名,但是一般不建议这么做。在同时需要导入最终包名相同的包如html/templatetext/template的时候倒是可以这样做。

1
2
import htmlTemplate "html/template
import "text/template"

也可以忽略掉导入的包,一般是用于第三方库中的依赖,但是自己写代码时用不到的包import _ "fmt"

2. 函数的定义

func 是定义函数的关键字,返回值类型写在后面。

1
2
3
func add(x int, y int) int {
return x + y
}

参数类型相同的话,可以把多个变量名写在前面,类型写在后面。

1
2
3
func add(x, y int) int {
return x+y
}

函数可以有多个返回值,error通常是最后一个返回值。

1
2
3
func swap(x, y, z string) (string, string, string) {
return z, x, y
}

返回值里面有变量名的话,函数体里面不用再次声明。

1
2
3
4
5
6
func split(sum int) (x, y int) {
x = sum * 4/9
y = sum - x
return
//return x, y 这样写也可以
}

内部函数

1
2
3
4
5
6
func main() {
hypot := func(x, y float64) float64 {
return math.Sqrt(x*x + y*y)
}
fmt.Println(hypot(3, 4))
}

3.变量

Go没有使用public、private之类的关键字定义变量的可访问性,Go是通过变量名的第一个字母是否大写这种代码风格区别的,第一个字母是大写的话就相当于public,小写的话就是private。var定义的是全局变量或局部变量。:=声明与初始化局部变量,只能放在函数体里面。

1
2
3
var c, python, java bool
var c, python, java = true, false, "no!"
a, b, c := swap("hello", "world", "!")

const定义常量

1
2
3
4
5
const Pi = 3.14
const (
Big = 1<<100
Small = Big>>99
)

声明一个key是string, value是int类型的map

1
var m map[string]int

4. for 循环

比起其他语言,Go的for循环少了两个括号,但大括号是必须的。

1
2
3
4
sum :=0
for i := 0; i <10; i++ {
sum += i
}

缺省步长的形式,类似其他语言的while语句,Go语句是没有while关键字的,即没有while语句。

1
2
3
4
5
6
7
8
for ; sum < 1000; {
sum += sum
}

//两个分号可以不写
for sum <1000 {
sum += sum
}

死循环

1
2
3
4
5
6
for ; ; {
}

//或者也把分号省略掉
for {
}

5. if判断语句

与for类似if后面也是没有括号的,大括号是必须的。

1
2
3
if x < 0 {
fmt.Println(x)
}

多个判断条件,可以使用&&或者||

1
2
3
if x >0 && x < 100 {
fmt.Println(x)
}

ifelse ifelse的形式

1
2
3
4
5
6
7
8
9
10
if v := math.Pow(x, n) ; v < lim {
return v
} else if v > lim {
return lim
} else {
return 0
}
//变量 v 的作用范围到这里就结束了,
//就算不return后面的语句如果直接使用v如:
//fmt.Println(v)会报 “undefinded: v” 的错误

6. 基本的数据类型

1
2
3
4
5
6
7
bool
string
int int8 int16 int32 int64
uint unit8 unit16 uint64 uintptr
byte // uint8的别名
float32 float64
complex64 complex128

声明变量并初始化

1
2
3
4
5
var (
ToBe bool = false
MaxInt uint64 = 1<<64 - 1
z complex128 = cmplx.Sqrt(-5+12i)
)

可以用这种方法定义枚举

1
2
3
4
5
const (
red = iota // red == 0
blue // blue == 1
green // green == 2
)

7. 结构体

使用type关键字定义结构体,Vertex是结构体的名称

1
2
3
4
type Vertex struct {
X int
Y int
}

初始化结构体

  • v都是值对象,不是指针

    1
    2
    3
    4
    5
    v := Vertex{1, 2}
    v := Vertex{X: 1} //Y没有赋值,默认是0
    v := Vertex{}
    v.X = 4
    fmt.Println(v.X)
  • t是指针,Go是有指针的,但是没有指针运算

    1
    2
    3
    var t *Vertex = new(Vertex)
    t := new(Vertex)
    t := &Vertex{} //t的类型是*Vertex

8. 指针

与C语言类似,Go是有指针的,但是没有指针运算

1
2
3
4
5
6
7
8
9
type Vertex struct {
X, Y int
}

p := Vertex{1, 2} //p是值对象
q := &p //&取地址, q是指针
q.X = 1000
fmt.Println(p)
fmt.Println(*p) //* 取值

9. map

type Vertex struct { x, y int}
//m是变量名,string是key的类型,Vertex是value的类型
var m map[string]Vertex

初始化map是用make而不是new

1
2
3
4
m = make(map[string]Vertex)
m = map[string]Vertex{"Bell Labs" : Vertex{40.12, -34.56},
"Go" : Vertex{37.43, -123.45},
}

赋值

1
m["GZ"] = Vertex{23.56, 56.23}

删除,使用系统内置的delete方法

1
delete(m, "GZ")

测试key是否存在,ok是true表示存在,是false表示不存在。

1
elem, ok = m["GZ"]

10. Array & Slice

Array是指包含同一类型的数据集合,数组中的元素必须是同一类型的,数组的长度要确定(无论是显式声明或者编译时计算确定),数组的长度定义好之后就不能修改了。
数组的声明:

[1024]byte //长度为1024的数组
[10] *int64 //长度为10的指针数组
[3][4]int //二维数组

数组的长度可以用len()函数获取,len(array)。
数组的遍历

1
2
3
for i, v := range array {
fmt.Println("Array element[", i, "]=", v)
}

数组是值类型的,当数组作为参数传递的时候是值传递。也就是在函数体中修改了传进来的数组参数,在函数体外不生效。要想在函数体外生效的话就用slice, slice就像是指向数组的指针。事实上slice的结构包括一个指向数组的指针、元素个数len、分配空间大小cap。
slice的创建,只是定义slice但是不初始化的话,slice是nil。

1
2
p := []int{2, 3, 5, 7, 11, 13}
var z []int //z == nil

可以根据slice创建新的slice,创建形式是s[low:hight],元素包括low,不包hight,且low是从0开始计数。

1
2
3
4
5
p := []int{2, 3, 5, 7, 11, 13}
fmt.Println("p[1:4] ==", p[1:4]) //output: p[1:4] == [3 5 7]
fmt.Println("p[:3] ==", p[:3]) //output: p[:3] == [2 3 5]
fmt.Println("p[4:] ==", p[4:]) //output: p[4:] == [11 13]
fmt.Println("p[4:] ==", p[4:4]) //output: p[4:4] == []

也可以使用make()创建,make()创建的slice元素的初始值是0

1
2
3
p := make([]int, 5) //len(a)=5
fmt.Println("p == ",p) //output: p == [0 0 0 0 0]
p := make([]int, 0, 5) // len(p)=0, cap(p)=5

Slice的增减元素,删除元素的话可以使用s[low:hight]创建新的slice,添加元素的话可以使用append()。

1
2
3
4
p := []int{2, 3, 5, 7, 11, 13}
p = append(p, 1, 2, 3)
pp := []int{1, 3, 9}
p = append(p, pp...)

append()是个不定参数,可以将1个或多个元素添加到slice,但是如果要将一个slice添加到另一个slice,作为第二个参数的slice后面必须添加”…”这样的3个点。如p = append(p, pp...),否则会报错。

11. switch

switch是根据传入的条件不同,会按顺序选择执行不同的语句,如果没有条件满足就是执行default中的语句。
下面的例子是根据运行环境的不同打印出当前运行的是什么操作系统:

1
2
3
4
5
6
7
8
switch os := runtime.GOOS; os {
case "darwin":
fmt.Println("OS X.")
case "linux":
fmt.Println("Linux.")
default:
fmt.Printf("%s.", os)
}

switch也可以没有判断条件,这样的话就等效与if…else…

1
2
3
4
5
6
7
8
9
10
11
12
t := time.Now()
switch {
case t.Hour() < 12:
fmt.Println("Good morning!")
case t.Hour() < 25:
fmt.Println("Good afternoon.")
fallthrough
case t.Hour() < 25:
fmt.Println("Good afternoon.")
default:
fmt.Println("Good evening.")
}

switch默认是选择到需要执行的语句之后,后面的语句是不会再进行判断执行的。但是可以使用fallthrough强制判断后面的语句是否需要执行。比如上面的例子会强制输出两次”Good afternoon.”。