go学习笔记:1.基本概念

参考文献列表:
http://tour.golang.org/

安装 Golang

在 http://golang.org/dl/ 可以下载到 Golang。安装文档:http://golang.org/doc/install

Hello Go

我们先创建一个文件 hello.go:

  
  
  1. package main
  2.  
  3. import "fmt"
  4.  
  5. func main() {
  6. fmt.Printf("hello Golang\n");
  7. }

执行此程序:

  
  
  1. go run hello.go

Golang 程序由包(packages)组成,程序从 main 包开始运行:

  
  
  1. package main

此语句表示此文件属于 main 包(多个源文件可以属于同一个包)。import 语句后接上包所在的路径(被叫做包路径或导入路径),一个目录中放置一个包,通常的做法是,目录名和包名相同:

  
  
  1. import (
  2. "fmt"
  3. "math/rand"
  4. )

这里的 “fmt” 和 “math/rand” 为包路径(导入路径)。上面的 import 语句也可以这样写:

  
  
  1. import "fmt"
  2. import "math/rand"

我们导入了包之后,就可以通过 “包名.name” 来引用导出的 name 了,例如:

  
  
  1. import "fmt"
  2.  
  3. // fmt 包导出了 Printf
  4. fmt.Printf("hello Golang\n");

在 Golang 中,一个名字如果首字母大写则表示此名字被导出。

函数
  
  
  1. package main
  2.  
  3. import "fmt"
  4.  
  5. func add(x int, y int) int {
  6. return x + y
  7. }
  8.  
  9. func main() {
  10. fmt.Println(add(42, 13))
  11. }

需要注意的就是,变量名在类型之前,这和很多语言都不一样。另外 x int, y int 也可以写为 x, y int:

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

函数可以返回多个值:

  
  
  1. package main
  2.  
  3. import "fmt"
  4.  
  5. func swap(x, y string) (string, string) {
  6. return y, x
  7. }
  8.  
  9. func main() {
  10. a, b := swap("hello", "world")
  11. fmt.Println(a, b)
  12. }

返回值可以被指定变量名,并且像变量一样使用:

  
  
  1. package main
  2.  
  3. import "fmt"
  4.  
  5. func split(sum int) (x, y int) {
  6. x = sum * 4 / 9
  7. y = sum - x
  8. return
  9. }
  10.  
  11. func main() {
  12. fmt.Println(split(17))
  13. }

可以看到 split 函数直接使用 return 语句而不用带参数。

变量

变量的声明使用 var 语句:

  
  
  1. var i int
  2. var c, python, java bool

变量在声明时可以进行初始化:

  
  
  1. var x, y int = 1, 2
  2. var i, j = true, "hello"

我们看到,初始化时可以指定也可以不指定变量类型。
按照 Golang 的语法,在函数外的任何结构(construct)都通过一个关键字开始,例如变量使用 var 关键字开始,函数使用 func 关键字开始。在函数内,变量赋值可以使用 := 操作符:

  
  
  1. package main
  2.  
  3. func main() {
  4. var x, y int = 1, 2
  5. i, j := true, "hello"
  6. }

:= 操作符左边为变量,右边为值。

数据类型

基本数据类型:

  1. bool
  2. string
  3. int int8 int16 int32 int64
  4. uint uint8 uint16 uint32 uint64
  5. uintptr
  6. byte(等价于 uint8)
  7. rune(等价于 int32,用于表示一个 unicode code point)
  8. float32 float64
  9. complex64 complex128

int 和 uint 的具体大小是实现相关的。Golang 的 64 位编译器在 Golang 1.1 之后使用 64 位表示 int。

类型转换使用表达式 T(v),含义为将 v 转换为类型 T:

  
  
  1. var i int = 42
  2. var f float64 = float64(i)
  3.  
  4. i := 42
  5. f := float64(i)

类型转换总需要显式的进行。

使用 const 来声明常量:

  
  
  1. const Pi = 3.14
  2.  
  3. const (
  4. Big = 1 << 100
  5. Small = Big >> 99
  6. )
控制语句
for 语句

Golang 使用(且只使用)for 来进行循环(没有 while 语句):

  
  
  1. package main
  2.  
  3. func main() {
  4. sum := 0
  5. for i := 0; i < 10; i++ {
  6. sum += i
  7. }
  8. // 这种写法等价于 C/C++ 等语言中的 while 语句
  9. for sum < 1000 {
  10. sum += sum
  11. }
  12. }

区别于 C/C++ 等语言,使用 for 语句时不需要 () 并且 {} 是必须的(后面谈到的 if、switch 在此语法处理上也是一样的)。如果需要无限循环,那么使用:

  
  
  1. for {
  2. }
if 语句

if 语句可以在执行条件判断前带一个语句(这常被叫做 if 带上一个短语句),此语句中变量的生命周期在 if 语句结束后结束。例如:

  
  
  1. package main
  2.  
  3. import (
  4. "fmt"
  5. "math/rand"
  6. )
  7.  
  8. func main() {
  9. if n := rand.Intn(6); n <= 2 {
  10. fmt.Println("[0, 2]", n)
  11. } else {
  12. fmt.Println("[3, 5]", n)
  13. }
  14.  
  15. // 这里开始无法使用变量 n
  16. }
switch
  
  
  1. package main
  2.  
  3. import (
  4. "fmt"
  5. "runtime"
  6. )
  7.  
  8. func main() {
  9. fmt.Print("Go runs on ")
  10. // switch 类似 if 可以带上一个短语句
  11. switch os := runtime.GOOS; os {
  12. case "darwin":
  13. fmt.Println("OS X.")
  14. case "linux":
  15. fmt.Println("Linux.")
  16. default:
  17. // freebsd, openbsd,
  18. // plan9, windows...
  19. fmt.Printf("%s.", os)
  20. }
  21. }

不像 C/C++ 等语言,Golang 中无需使用 break 语句来跳出 switch。另外,switch 可以没有条件:

  
  
  1. package main
  2.  
  3. import (
  4. "fmt"
  5. "time"
  6. )
  7.  
  8. func main() {
  9. t := time.Now()
  10. switch {
  11. case t.Hour() < 12:
  12. fmt.Println("Good morning!")
  13. case t.Hour() < 17:
  14. fmt.Println("Good afternoon.")
  15. default:
  16. fmt.Println("Good evening.")
  17. }
  18. }

特别的是,Golang 中有一个 fallthrough 语句,用于转移控制到下一个 case 的第一条语句(注意,这意味着直接跳过下一个 case 的条件判断):

  
  
  1. // C/C++ 中
  2. #include <stdio.h>
  3.  
  4. int main() {
  5. int n = 2;
  6.  
  7. switch (n) {
  8. case 1:
  9. case 2:
  10. case 3:
  11. printf("1 or 2 or 3");
  12. break;
  13. case 4:
  14. case 5:
  15. case 6:
  16. printf("4 or 5 or 6");
  17. break;
  18. }
  19.  
  20. return 0;
  21. }

对应的实现:

  
  
  1. // Golang 中
  2. package main
  3.  
  4. import (
  5. "fmt"
  6. )
  7.  
  8. func main() {
  9. n := 2
  10.  
  11. switch n {
  12. case 1:
  13. fallthrough
  14. case 2:
  15. fallthrough
  16. case 3:
  17. fmt.Printf("1 or 2 or 3")
  18. case 4:
  19. fallthrough
  20. case 5:
  21. fallthrough
  22. case 6:
  23. fmt.Printf("4 or 5 or 6")
  24. }
  25. }

还有更简单的写法:

  
  
  1. package main
  2.  
  3. import (
  4. "fmt"
  5. )
  6.  
  7. func main() {
  8. n := 2
  9.  
  10. switch n {
  11. case 1, 2, 3:
  12. fmt.Printf("1 or 2 or 3")
  13. case 4, 5, 6:
  14. fmt.Printf("4 or 5 or 6")
  15. }
  16. }
defer

一个 defer 语句能够将一个函数调用加入一个列表中(这个函数调用被叫做 deferred 函数调用),在当前函数调用结束时调用列表中的函数。范例:

  
  
  1. func CopyFile(dstName, srcName string) (written int64, err error) {
  2. src, err := os.Open(srcName)
  3. if err != nil {
  4. return
  5. }
  6. defer src.Close()
  7.  
  8. dst, err := os.Create(dstName)
  9. if err != nil {
  10. return
  11. }
  12. defer dst.Close()
  13.  
  14. return io.Copy(dst, src)
  15. }

deferred 函数调用按先进后出的顺序执行:

  
  
  1. package main
  2.  
  3. import "fmt"
  4.  
  5. func main() {
  6. for i := 0; i < 5; i++ {
  7. // 输出 43210
  8. defer fmt.Print(i)
  9. }
  10. }

defer 还常常和 panic、recover 配合使用,具体参考:http://name5566.com/5157.html

结构(structs)

结构是一个域的集合:

  
  
  1. package main
  2.  
  3. import "fmt"
  4.  
  5. type Vertex struct {
  6. X int
  7. Y int
  8. }
  9.  
  10. func main() {
  11. v := Vertex{1, 2}
  12. v.X = 4
  13. fmt.Println(v)
  14. }

Golang 中是存在指针的,但是指针不支持算术运算:

  
  
  1. p := Vertex{1, 2} // {1, 2} 为 struct literal
  2. q := &p // q 类型为 *Vertex
  3. q.X = 2 // 直接访问域 X

就像上面看到的,struct 的 literal 由 {} 包裹,在 struct literal 中我们可以使用 Name: 这样的语法来为特定域设置值:

  
  
  1. type Vertex struct {
  2. X, Y int
  3. }
  4.  
  5. r := Vertex{X: 3} // 这时候 Y 为 0
new 函数

我们可以通过表达式 new(T) 分配一个被初始化为 0 且类型为 T 的值,并且返回指向此值的指针,用法如下:

  
  
  1. var p *T = new(T)
  2. p := new(T)

更详尽的例子:

  
  
  1. package main
  2.  
  3. import "fmt"
  4.  
  5. type Vertex struct {
  6. X, Y int
  7. }
  8.  
  9. func main() {
  10. v := new(Vertex)
  11. fmt.Println(v)
  12. v.X, v.Y = 11, 9
  13. fmt.Println(v)
  14. }
数组和 slice

[n]T 在 Golang 中是一个类型(就像 *T 一样),表示一个长度为 n 的数组其元素类型为 T。范例:

  
  
  1. package main
  2.  
  3. import "fmt"
  4.  
  5. func main() {
  6. var a [2]string
  7. a[0] = "Hello"
  8. a[1] = "World"
  9. fmt.Println(a[0], a[1])
  10. fmt.Println(a)
  11. }

注意,数组长度无法被改变。

slice 是一个数据结构,其指向一个数组某个连续的部分。slice 用起来很像数组。[]T 为 slice 类型,其中元素类型为 T:

  
  
  1. package main
  2.  
  3. import "fmt"
  4.  
  5. func main() {
  6. // 构建一个 slice
  7. p := []int{2, 3, 5, 7, 11, 13}
  8. fmt.Println("p ==", p)
  9.  
  10. for i := 0; i < len(p); i++ {
  11. fmt.Printf("p[%d] == %d\n", i, p[i])
  12. }
  13. }

表达式 s[lo:hi] 用于创建一个 slice,新创建的 slice 的元素为 s 中 [lo, hi) 位置的元素。

创建 slice 使用 make 函数(而不是用 new 函数,因为需要设置额外的参数来控制 slice 的创建):

  
  
  1. // len(a) 为 5
  2. a := make([]int, 5)

这里 make 函数会创建一个数组(其元素初始化为 0)并返回一个 slice 指向此数组。make 可以带第三个参数,用于指定容量:

  
  
  1. // len(b) 为 0
  2. // cap(b) 为 5
  3. b := make([]int, 0, 5)
  4.  
  5. b = b[:cap(b)] // len(b)=5, cap(b)=5
  6. b = b[1:] // len(b)=4, cap(b)=4

一个没有值的 slice 是 nil,长度和容量都为 0。

  
  
  1. package main
  2.  
  3. import "fmt"
  4.  
  5. func main() {
  6. var z []int
  7. fmt.Println(z, len(z), cap(z))
  8. if z == nil {
  9. fmt.Println("nil!")
  10. }
  11. }

提及一下的是,new 在 Golang 中被用于分配内存,make 用于初始化 slice、map、channel 类型。

range

range 被用在 for 中来迭代一个 slice 或者一个 map:

  
  
  1. package main
  2.  
  3. import "fmt"
  4.  
  5. var s = []int{1, 2, 3}
  6.  
  7. func main() {
  8. for i, v := range s {
  9. fmt.Println(i, v)
  10. }
  11.  
  12. // 只需要值,使用 _ 忽略索引
  13. for _, v := range s {
  14. fmt.Println(v)
  15. }
  16.  
  17. // 只需要索引
  18. for i := range s {
  19. fmt.Println(i)
  20. }
  21. }
map

map 用于映射 key 到 value(值)。map 可以通过 make 来创建(而非 new):

  
  
  1. package main
  2.  
  3. import "fmt"
  4.  
  5. type Vertex struct {
  6. Lat, Long float64
  7. }
  8.  
  9. var m map[string]Vertex
  10.  
  11. func main() {
  12. m = make(map[string]Vertex)
  13. m["Bell Labs"] = Vertex{
  14. 40.68433, -74.39967,
  15. }
  16. fmt.Println(m["Bell Labs"])
  17. }

map iteral 很像 struct literal:

  
  
  1. var m = map[string]Vertex{
  2. // 这里 Vertex 可以省略不写
  3. "Bell Labs": Vertex{
  4. 40.68433, -74.39967,
  5. },
  6. "Google": Vertex{
  7. 37.42202, -122.08408,
  8. },
  9. }

使用 [] 来访问 map 中的值,使用 delete 来删除 map 中的值:

  
  
  1. m[key] = elem
  2. elem = m[key]
  3. delete(m, key)

如果需要检查 map 中某 key 是否存在使用:

  
  
  1. elem, ok = m[key]

elem 表示 key 的值(key 不存在时,elem 为 0),ok 表示 key 是否存在。

闭包

Golang 中函数也是一个值(就像 int 值一样),且函数可以是一个闭包。闭包是一个引用了外部变量的函数。看一个例子:

  
  
  1. package main
  2.  
  3. import "fmt"
  4.  
  5. func adder() func(int) int {
  6. sum := 0
  7. // 返回一个闭包,此闭包引用了外部变量 sum
  8. return func(x int) int {
  9. sum += x
  10. return sum
  11. }
  12. }
  13.  
  14. func main() {
  15. a := adder()
  16. fmt.Println(a(9527))
  17. }
转自:http://name5566.com/4813.html
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值