参考文献列表:
http://tour.golang.org/
安装 Golang
在 http://golang.org/dl/ 可以下载到 Golang。安装文档:http://golang.org/doc/install。
Hello Go
我们先创建一个文件 hello.go:
- package main
- import "fmt"
- func main() {
- fmt.Printf("hello Golang\n");
- }
执行此程序:
- go run hello.go
包
Golang 程序由包(packages)组成,程序从 main 包开始运行:
- package main
此语句表示此文件属于 main 包(多个源文件可以属于同一个包)。import 语句后接上包所在的路径(被叫做包路径或导入路径),一个目录中放置一个包,通常的做法是,目录名和包名相同:
- import (
- "fmt"
- "math/rand"
- )
这里的 “fmt” 和 “math/rand” 为包路径(导入路径)。上面的 import 语句也可以这样写:
- import "fmt"
- import "math/rand"
我们导入了包之后,就可以通过 “包名.name” 来引用导出的 name 了,例如:
- import "fmt"
- // fmt 包导出了 Printf
- fmt.Printf("hello Golang\n");
在 Golang 中,一个名字如果首字母大写则表示此名字被导出。
函数
- package main
- import "fmt"
- func add(x int, y int) int {
- return x + y
- }
- func main() {
- fmt.Println(add(42, 13))
- }
需要注意的就是,变量名在类型之前,这和很多语言都不一样。另外 x int, y int 也可以写为 x, y int:
- func add(x, y int) int {
- return x + y
- }
函数可以返回多个值:
- package main
- import "fmt"
- func swap(x, y string) (string, string) {
- return y, x
- }
- func main() {
- a, b := swap("hello", "world")
- fmt.Println(a, b)
- }
返回值可以被指定变量名,并且像变量一样使用:
- package main
- import "fmt"
- func split(sum int) (x, y int) {
- x = sum * 4 / 9
- y = sum - x
- return
- }
- func main() {
- fmt.Println(split(17))
- }
可以看到 split 函数直接使用 return 语句而不用带参数。
变量
变量的声明使用 var 语句:
- var i int
- var c, python, java bool
变量在声明时可以进行初始化:
- var x, y int = 1, 2
- var i, j = true, "hello"
我们看到,初始化时可以指定也可以不指定变量类型。
按照 Golang 的语法,在函数外的任何结构(construct)都通过一个关键字开始,例如变量使用 var 关键字开始,函数使用 func 关键字开始。在函数内,变量赋值可以使用 := 操作符:
- package main
- func main() {
- var x, y int = 1, 2
- i, j := true, "hello"
- }
:= 操作符左边为变量,右边为值。
数据类型
基本数据类型:
- bool
- string
- int int8 int16 int32 int64
- uint uint8 uint16 uint32 uint64
- uintptr
- byte(等价于 uint8)
- rune(等价于 int32,用于表示一个 unicode code point)
- float32 float64
- complex64 complex128
int 和 uint 的具体大小是实现相关的。Golang 的 64 位编译器在 Golang 1.1 之后使用 64 位表示 int。
类型转换使用表达式 T(v),含义为将 v 转换为类型 T:
- var i int = 42
- var f float64 = float64(i)
- i := 42
- f := float64(i)
类型转换总需要显式的进行。
使用 const 来声明常量:
- const Pi = 3.14
- const (
- Big = 1 << 100
- Small = Big >> 99
- )
控制语句
for 语句
Golang 使用(且只使用)for 来进行循环(没有 while 语句):
- package main
- func main() {
- sum := 0
- for i := 0; i < 10; i++ {
- sum += i
- }
- // 这种写法等价于 C/C++ 等语言中的 while 语句
- for sum < 1000 {
- sum += sum
- }
- }
区别于 C/C++ 等语言,使用 for 语句时不需要 () 并且 {} 是必须的(后面谈到的 if、switch 在此语法处理上也是一样的)。如果需要无限循环,那么使用:
- for {
- }
if 语句
if 语句可以在执行条件判断前带一个语句(这常被叫做 if 带上一个短语句),此语句中变量的生命周期在 if 语句结束后结束。例如:
- package main
- import (
- "fmt"
- "math/rand"
- )
- func main() {
- if n := rand.Intn(6); n <= 2 {
- fmt.Println("[0, 2]", n)
- } else {
- fmt.Println("[3, 5]", n)
- }
- // 这里开始无法使用变量 n
- }
switch
- package main
- import (
- "fmt"
- "runtime"
- )
- func main() {
- fmt.Print("Go runs on ")
- // switch 类似 if 可以带上一个短语句
- switch os := runtime.GOOS; os {
- case "darwin":
- fmt.Println("OS X.")
- case "linux":
- fmt.Println("Linux.")
- default:
- // freebsd, openbsd,
- // plan9, windows...
- fmt.Printf("%s.", os)
- }
- }
不像 C/C++ 等语言,Golang 中无需使用 break 语句来跳出 switch。另外,switch 可以没有条件:
- package main
- import (
- "fmt"
- "time"
- )
- func main() {
- t := time.Now()
- switch {
- case t.Hour() < 12:
- fmt.Println("Good morning!")
- case t.Hour() < 17:
- fmt.Println("Good afternoon.")
- default:
- fmt.Println("Good evening.")
- }
- }
特别的是,Golang 中有一个 fallthrough 语句,用于转移控制到下一个 case 的第一条语句(注意,这意味着直接跳过下一个 case 的条件判断):
- // C/C++ 中
- #include <stdio.h>
- int main() {
- int n = 2;
- switch (n) {
- case 1:
- case 2:
- case 3:
- printf("1 or 2 or 3");
- break;
- case 4:
- case 5:
- case 6:
- printf("4 or 5 or 6");
- break;
- }
- return 0;
- }
对应的实现:
- // Golang 中
- package main
- import (
- "fmt"
- )
- func main() {
- n := 2
- switch n {
- case 1:
- fallthrough
- case 2:
- fallthrough
- case 3:
- fmt.Printf("1 or 2 or 3")
- case 4:
- fallthrough
- case 5:
- fallthrough
- case 6:
- fmt.Printf("4 or 5 or 6")
- }
- }
还有更简单的写法:
- package main
- import (
- "fmt"
- )
- func main() {
- n := 2
- switch n {
- case 1, 2, 3:
- fmt.Printf("1 or 2 or 3")
- case 4, 5, 6:
- fmt.Printf("4 or 5 or 6")
- }
- }
defer
一个 defer 语句能够将一个函数调用加入一个列表中(这个函数调用被叫做 deferred 函数调用),在当前函数调用结束时调用列表中的函数。范例:
- func CopyFile(dstName, srcName string) (written int64, err error) {
- src, err := os.Open(srcName)
- if err != nil {
- return
- }
- defer src.Close()
- dst, err := os.Create(dstName)
- if err != nil {
- return
- }
- defer dst.Close()
- return io.Copy(dst, src)
- }
deferred 函数调用按先进后出的顺序执行:
- package main
- import "fmt"
- func main() {
- for i := 0; i < 5; i++ {
- // 输出 43210
- defer fmt.Print(i)
- }
- }
defer 还常常和 panic、recover 配合使用,具体参考:http://name5566.com/5157.html
结构(structs)
结构是一个域的集合:
- package main
- import "fmt"
- type Vertex struct {
- X int
- Y int
- }
- func main() {
- v := Vertex{1, 2}
- v.X = 4
- fmt.Println(v)
- }
Golang 中是存在指针的,但是指针不支持算术运算:
- p := Vertex{1, 2} // {1, 2} 为 struct literal
- q := &p // q 类型为 *Vertex
- q.X = 2 // 直接访问域 X
就像上面看到的,struct 的 literal 由 {} 包裹,在 struct literal 中我们可以使用 Name: 这样的语法来为特定域设置值:
- type Vertex struct {
- X, Y int
- }
- r := Vertex{X: 3} // 这时候 Y 为 0
new 函数
我们可以通过表达式 new(T) 分配一个被初始化为 0 且类型为 T 的值,并且返回指向此值的指针,用法如下:
- var p *T = new(T)
- p := new(T)
更详尽的例子:
- package main
- import "fmt"
- type Vertex struct {
- X, Y int
- }
- func main() {
- v := new(Vertex)
- fmt.Println(v)
- v.X, v.Y = 11, 9
- fmt.Println(v)
- }
数组和 slice
[n]T 在 Golang 中是一个类型(就像 *T 一样),表示一个长度为 n 的数组其元素类型为 T。范例:
- package main
- import "fmt"
- func main() {
- var a [2]string
- a[0] = "Hello"
- a[1] = "World"
- fmt.Println(a[0], a[1])
- fmt.Println(a)
- }
注意,数组长度无法被改变。
slice 是一个数据结构,其指向一个数组某个连续的部分。slice 用起来很像数组。[]T 为 slice 类型,其中元素类型为 T:
- package main
- import "fmt"
- func main() {
- // 构建一个 slice
- p := []int{2, 3, 5, 7, 11, 13}
- fmt.Println("p ==", p)
- for i := 0; i < len(p); i++ {
- fmt.Printf("p[%d] == %d\n", i, p[i])
- }
- }
表达式 s[lo:hi] 用于创建一个 slice,新创建的 slice 的元素为 s 中 [lo, hi) 位置的元素。
创建 slice 使用 make 函数(而不是用 new 函数,因为需要设置额外的参数来控制 slice 的创建):
- // len(a) 为 5
- a := make([]int, 5)
这里 make 函数会创建一个数组(其元素初始化为 0)并返回一个 slice 指向此数组。make 可以带第三个参数,用于指定容量:
- // len(b) 为 0
- // cap(b) 为 5
- b := make([]int, 0, 5)
- b = b[:cap(b)] // len(b)=5, cap(b)=5
- b = b[1:] // len(b)=4, cap(b)=4
一个没有值的 slice 是 nil,长度和容量都为 0。
- package main
- import "fmt"
- func main() {
- var z []int
- fmt.Println(z, len(z), cap(z))
- if z == nil {
- fmt.Println("nil!")
- }
- }
提及一下的是,new 在 Golang 中被用于分配内存,make 用于初始化 slice、map、channel 类型。
range
range 被用在 for 中来迭代一个 slice 或者一个 map:
- package main
- import "fmt"
- var s = []int{1, 2, 3}
- func main() {
- for i, v := range s {
- fmt.Println(i, v)
- }
- // 只需要值,使用 _ 忽略索引
- for _, v := range s {
- fmt.Println(v)
- }
- // 只需要索引
- for i := range s {
- fmt.Println(i)
- }
- }
map
map 用于映射 key 到 value(值)。map 可以通过 make 来创建(而非 new):
- package main
- import "fmt"
- type Vertex struct {
- Lat, Long float64
- }
- var m map[string]Vertex
- func main() {
- m = make(map[string]Vertex)
- m["Bell Labs"] = Vertex{
- 40.68433, -74.39967,
- }
- fmt.Println(m["Bell Labs"])
- }
map iteral 很像 struct literal:
- var m = map[string]Vertex{
- // 这里 Vertex 可以省略不写
- "Bell Labs": Vertex{
- 40.68433, -74.39967,
- },
- "Google": Vertex{
- 37.42202, -122.08408,
- },
- }
使用 [] 来访问 map 中的值,使用 delete 来删除 map 中的值:
- m[key] = elem
- elem = m[key]
- delete(m, key)
如果需要检查 map 中某 key 是否存在使用:
- elem, ok = m[key]
elem 表示 key 的值(key 不存在时,elem 为 0),ok 表示 key 是否存在。
闭包
Golang 中函数也是一个值(就像 int 值一样),且函数可以是一个闭包。闭包是一个引用了外部变量的函数。看一个例子:
- package main
- import "fmt"
- func adder() func(int) int {
- sum := 0
- // 返回一个闭包,此闭包引用了外部变量 sum
- return func(x int) int {
- sum += x
- return sum
- }
- }
- func main() {
- a := adder()
- fmt.Println(a(9527))
- }
转自:http://name5566.com/4813.html