Skip to content

jokerhistory/Go-zh

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

9 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Go-zh

本文档展示了一个简单Go包的开发,并介绍了用go工具来获取、 构建并安装Go包及命令的标准方式。

go 工具需要你按照指定的方式来组织代码。请仔细阅读本文档, 它说明了如何以最简单的方式来准备并运行你的Go安装。

类似的视频讲解可在此处观看。 http://www.youtube.com/watch?v=XCsL89YtqCs

https://go-zh.org/doc/code.html 引言 代码的组织 工作空间 GOPATH 环境变量 包路径 你的第一个程序 你的第一个库 包名 测试 远程包 接下来做什么 获取帮助

#实效Go编程 https://go-zh.org/doc/effective_go.html#%E5%90%8D%E7%A7%B0

#switch 的执行顺序 switch 的条件从上到下的执行,当匹配成功的时候停止。

(例如,

switch i { case 0: case f(): } 当 i==0 时不会调用 f 。) ###没有条件的 switch 没有条件的 switch 同 switch true 一样。

这一构造使得可以用更清晰的形式来编写长的 if-then-else 链。

#defer defer 语句会延迟函数的执行直到上层函数返回。

延迟调用的参数会立刻生成,但是在上层函数返回前函数都不会被调用。 ###defer 栈 (先进后出) 延迟的函数调用被压入一个栈中。当函数返回时, 会按照后进先出的顺序调用被延迟的函数调用。

阅读博文了解更多关于 defer 语句的信息。 https://blog.go-zh.org/defer-panic-and-recover

#指针 https://tour.go-zh.org/moretypes/1

指针 Go 具有指针。 指针保存了变量的内存地址。

类型 *T 是指向类型 T 的值的指针。其零值是 nil 。

var p *int & 符号会生成一个指向其作用对象的指针。

i := 42 p = &i

  • 符号表示指针指向的底层的值。

fmt.Println(*p) // 通过指针 p 读取 i *p = 21 // 通过指针 p 设置 i 这也就是通常所说的“间接引用”或“非直接引用”。

与 C 不同,Go 没有指针运算。

结构体指针 结构体字段可以通过结构体指针来访问。

通过指针间接的访问是透明的。

package main

import "fmt"

type Vertex struct { X int Y int }

func main() { v := Vertex{1, 2} p := &v //生成指针的用法 p.X = 1e9 fmt.Println(v)

p  = &Vertex{1, 2} // 类型为 *Vertex
fmt.Println(v1, p, v2, v3)

}

###结构体文法 结构体文法表示通过结构体字段的值作为列表来新分配一个结构体。

使用 Name: 语法可以仅列出部分字段。(字段名的顺序无关。)

特殊的前缀 & 返回一个指向结构体的指针。


#数组 类型 [n]T 是一个有 n 个类型为 T 的值的数组。

表达式

var a [10]int 定义变量 a 是一个有十个整数的数组。

数组的长度是其类型的一部分,因此数组不能改变大小。 这看起来是一个制约,但是请不要担心; Go 提供了更加便利的方式来使用数组。 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 是一个元素类型为 T 的 slice。

len(s) 返回 slice s 的长度。

package main

import "fmt"

###对 slice 切片 slice 可以重新切片,创建一个新的 slice 值指向相同的数组。

func main() { s := []int{2, 3, 5, 7, 11, 13} fmt.Println("s ==", s) fmt.Println("s[1:4] ==", s[1:4])

// 省略下标代表从 0 开始
fmt.Println("s[:3] ==", s[:3])

// 省略上标代表到 len(s) 结束
fmt.Println("s[4:] ==", s[4:])

}

func main() { a := make([]int, 5) printSlice("a", a) b := make([]int, 0, 5) printSlice("b", b) //e := b[:cap(b)] //printSlice("e", e) c := b[:2] printSlice("c", c) d := c[2:5] printSlice("d", d) }

func printSlice(s string, x []int) { fmt.Printf("%s len=%d cap=%d %v\n", s, len(x), cap(x), x) }

nil slice slice 的零值是 nil 。

一个 nil 的 slice 的长度和容量是 0。

var z []int
fmt.Println(z, len(z), cap(z))
if z == nil {
	fmt.Println("nil!")
}

###向 slice 添加元素 向 slice 的末尾添加元素是一种常见的操作,因此 Go 提供了一个内建函数 append 。 内建函数的文档对 append 有详细介绍。

func append(s []T, vs ...T) []T append 的第一个参数 s 是一个元素类型为 T 的 slice ,其余类型为 T 的值将会附加到该 slice 的末尾。

append 的结果是一个包含原 slice 所有元素加上新添加的元素的 slice。

如果 s 的底层数组太小,而不能容纳所有值时,会分配一个更大的数组。 返回的 slice 会指向这个新分配的数组。

(了解更多关于 slice 的内容,参阅文章Go 切片:用法和本质。) https://blog.go-zh.org/go-slices-usage-and-internals

#range for 循环的 range 格式可以对 slice 或者 map 进行迭代循环。

当使用 for 循环遍历一个 slice 时,每次迭代 range 将返回两个值。 第一个是当前下标(序号),第二个是该下标所对应元素的一个拷贝。

package main

import "fmt"

var pow = []int{1, 2, 4, 8, 16, 32, 64, 128}

func main() { for i, v := range pow { fmt.Printf("2**%d = %d\n", i, v) } }

#map map 映射键到值。

map 在使用之前必须用 make 来创建;值为 nil 的 map 是空的,并且不能对其赋值

package main

import "fmt"

type Vertex struct { Lat, Long float64 }

var m map[string]Vertex var w map[string]int

func main() { m = make(map[string]Vertex) m["Bell Labs"] = Vertex{ 40.68433, -74.39967, } fmt.Println(m["Bell Labs"]) test()

}

func test() { w = make(map[string]int) w["Bell Labs"] = 1234 fmt.Println(w["Bell Labs"]) }intln(m["Bell Labs"]) }

###map 的文法 map 的文法跟结构体文法相似,不过必须有键名。

修改 map 在 map m 中插入或修改一个元素:

m[key] = elem 获得元素:

elem = m[key] 删除元素:

delete(m, key) 通过双赋值检测某个键存在:

elem, ok = m[key] 如果 key 在 m 中, ok 为 true。否则, ok 为 false,并且 elem 是 map 的元素类型的零值。

同样的,当从 map 中读取某个不存在的键时,结果是 map 的元素类型的零值。

m := make(map[string]int)

m["Answer"] = 42
fmt.Println("The value:", m["Answer"])

m["Answer"] = 48
fmt.Println("The value:", m["Answer"])

delete(m, "Answer")
fmt.Println("The value:", m["Answer"])

v, ok := m["Answer"]
fmt.Println("The value:", v, "Present?", ok)

练习: https://tour.go-zh.org/moretypes/20

#函数值 函数也是值。他们可以像其他值一样传递,比如,函数值可以作为函数的参数或者返回值。 package main

import ( "fmt" "math" )

func compute(aaa func(float64, float64) float64) float64 { return aaa(3, 4) }

func main() { hypot := func(x, y float64) float64 { return math.Sqrt(xx + yy) } fmt.Println(hypot(5, 12))

fmt.Println(compute(hypot))
fmt.Println(compute(math.Pow))

}

###函数的闭包 Go 函数可以是一个闭包。闭包是一个函数值,它引用了函数体之外的变量。 这个函数可以对这个引用的变量进行访问和赋值;换句话说这个函数被“绑定”在这个变量上。

例如,函数 adder 返回一个闭包。每个返回的闭包都被绑定到其各自的 sum 变量上。

package main

import "fmt"

func adder() func(int) int { sum := 0 return func(x int) int { sum += x return sum } }

func main() { pos, neg := adder(), adder() for i := 0; i < 2; i++ { fmt.Println( pos(i), neg(-2*i), ) } }

#方法

Go 没有类。然而,仍然可以在结构体类型上定义方法。

方法接收者 出现在 func 关键字和方法名之间的参数中。

package main

import ( "fmt" "math" )

type Vertex struct { X, Y float64 }

func (v Vertex) Abs() float64 { return math.Sqrt(v.Xv.X + v.Y*v.Y) }

func main() { v := &Vertex{3, 4} fmt.Println(v.Abs()) }

###接收者为指针的方法 方法可以与命名类型或命名类型的指针关联。

刚刚看到的两个 Abs 方法。一个是在 *Vertex 指针类型上,而另一个在 MyFloat 值类型上。 有两个原因需要使用指针接收者。首先避免在每个方法调用中拷贝值(如果值类型是大的结构体的话会更有效率)。其次,方法可以修改接收者指向的值。

当 v 是 Vertex 的时候 Scale 方法没有任何作用。Scale 修改 v。当 v 是一个值(非指针),方法看到的是 Vertex 的副本,并且无法修改原始值。

Abs 的工作方式是一样的。只不过,仅仅读取 v。所以读取的是原始值(通过指针)还是那个值的副本并没有关系。

package main

import ( "fmt" "math" )

type Vertex struct { X, Y float64 }

//尝试修改 Abs 的定义,同时 Scale 方法使用 Vertex 代替 *Vertex 作为接收者。

func (v *Vertex) Scale(f float64) { v.X = v.X * f v.Y = v.Y * f }

func (v Vertex) Abs() float64 { return math.Sqrt(v.Xv.X + v.Y*v.Y) }

func main() { v := &Vertex{3, 4} fmt.Printf("Before scaling: %+v, Abs: %v\n", v, v.Abs()) v.Scale(5) fmt.Printf("After scaling: %+v, Abs: %v\n", v, v.Abs()) }

#接口 接口类型是由一组方法定义的集合。

接口类型的值可以存放实现这些方法的任何值。

注意: 示例代码的 22 行存在一个错误。 由于 Abs 只定义在 *Vertex(指针类型)上, 所以 Vertex(值类型)不满足 Abser。 package main

import ( "fmt" "math" )

type Abser interface { Abs() float64 }

func main() { var a Abser f := MyFloat(-math.Sqrt2) v := Vertex{3, 4}

a = f  // a MyFloat 实现了 Abser
a = &v // a *Vertex 实现了 Abser

// 下面一行,v 是一个 Vertex(而不是 *Vertex)
// 所以没有实现 Abser。
a = v

fmt.Println(a.Abs())

}

type MyFloat float64

func (f MyFloat) Abs() float64 { if f < 0 { return float64(-f) } return float64(f) }

type Vertex struct { X, Y float64 }

func (v Vertex) Abs() float64 { return math.Sqrt(v.Xv.X + v.Y*v.Y) }

###隐式接口 类型通过实现那些方法来实现接口。 没有显式声明的必要;所以也就没有关键字“implements“。

隐式接口解藕了实现接口的包和定义接口的包:互不依赖。

因此,也就无需在每一个实现上增加新的接口名称,这样同时也鼓励了明确的接口定义。

包 io 定义了 Reader 和 Writer;其实不一定要这么做。

package main

import ( "fmt" "os" )

type Reader interface { Read(b []byte) (n int, err error) }

type Writer interface { Write(b []byte) (n int, err error) }

type ReadWriter interface { Reader Writer }

func main() { var w Writer

// os.Stdout 实现了 Writer
w = os.Stdout

fmt.Fprintf(w, "hello, writer\n")

}

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages