[菜鸟教程]Go语言笔记

194 阅读5分钟

(1)for循环

go语言有3种for循环的格式:

for i := 1; i <= 100; i++ {}
for i <= 100 {}
for {}

(2)range关键字

Go 语言中 range 关键字用于 for 循环中迭代数组(array)、切片(slice)、通道(channel)或集合(map)的元素。

使用range关键字,如果是两个(a,b):=range的话,那么一定是key,value;如果是a:=range的话,那么要么是index,要么是key。

所以go中的数组可以理解为index为int的map类型。(但是数组类型是有序的,map是无序的)

for k, v := range map {} 或者 for k := range 
for i, a := range array {} 或者 for i := range array {}

(3)make(type, len, cap)函数

使用make函数可以用来创建Type类型,长度为len,分配存储空间为cap的容器。

(4)map容器

  • Map 是无序的,遍历 Map 时返回的键值对的顺序是不确定的。

  • Map 是引用类型,如果将一个 Map 传递给一个函数或赋值给另一个变量,它们都指向同一个底层数据结构,因此对 Map 的修改会影响到所有引用它的变量。

  • 创建一个map[int]int的代码如下: m_map = make(map[int]int, len, cap) 或者 countryCapitalMap := map[string]string{"France": "Paris", "Italy": "Rome", "Japan": "Tokyo", "India": "New delhi"}

  • 获取一个map的长度 len(map)

  • 获取键值对

// 获取键值对
v1 := m["apple"]
v2, ok := m["pear"]  // 如果键不存在,ok 的值为 false,v2 的值为该类型的零值
  • 删除一对key,value : delete(map, key) 。比如delete(map, "banana")

(5)求平方根递归写法

func sqrtRecursive(x, guess, prevGuess, epsilon float64) float64 {  
        if diff := guess*guess - x; diff < epsilon && -diff < epsilon {  
                return guess  
        }  // if 语句里面的 condition 看最后的返回值 
  
        newGuess := (guess + x/guess) / 2  
        if newGuess == prevGuess {  
                return guess  
        }  
  
        return sqrtRecursive(x, newGuess, guess, epsilon)  
}  
  
func sqrt(x float64) float64 {  
        return sqrtRecursive(x, 1.00.01e-9)  
}

(6)Go 语言接口 interface

跟rsut语言的Trait是一样的。

(7)类型转换

类型转换用于将一种数据类型的变量转换为另外一种类型的变量。

Go 语言类型转换基本格式如下:

type_name(expression)

数值类型的转换

var a int = 10  
var b float64 = float64(a)

字符串和数值之间的转换

var str string = "10"
var num int
num, _ = strconv.Atoi(str)  
// 注意,strconv.Atoi 函数返回两个值,
// 第一个是转换后的整型值
// 第二个是可能发生的错误,我们可以使用空白标识符 _ 来忽略这个错误

接口类型转换

接口类型转换有两种情况 :类型断言类型转换

类型断言用于将接口类型转换为指定类型,其语法为:

value.(type) 
或者 
value.(T)

其中 value 是接口类型的变量,type 或 T 是要转换成的类型。

如果类型断言成功,它将返回转换后的值和一个布尔值,表示转换是否成功。

func main() {  
    var i interface{} = "Hello, World"  
    str, ok := i.(string)  
    if ok {  
        fmt.Printf("'%s' is a string\n", str)  
    } else {  
        fmt.Println("conversion failed")  
    }  
}

实例

type Writer interface {  
    Write([]byte) (interror)  // 所有的实例类都必须实现这个函数  
}  
  
type StringWriter struct {  
    str string  
}  
  
func (sw *StringWriter) Write(data []byte) (interror) {  
    sw.str += string(data)  
    return len(data), nil  
}  
  
func main() {  
    var w Writer = &StringWriter{}  
    sw := w.(*StringWriter)  
    sw.str = "Hello, World"  
    fmt.Println(sw.str)  
}

(8)golang变量的初始化

  • golang变量的初始化 - 知乎 (zhihu.com) - 有全局初始化和局部初始化的例子
  • 不同作用域类型的变量初始化顺序不同。
    • 对于全局变量来说,go编译器会采用拓扑排序的方式初始化变量,如果不存在一种合法的初始化方式,就会报错 initialize loop error
    • 对于局部变量、传参数的方式,会采用从上到下,从左到右的方式进行初始化
var ans int = 1

func get() int {
    ans += 1
    return ans
}

func print(a, b, c int) {
    fmt.Println(a, b, c)
}

func main() {
    print(get(), get(), get()) // 0 1 2 从左到右传递函数参数
}

(9)go语言中package执行顺序

image.png

(10)go语言中的通道 - 不同协程之间的通信

通道(channel)是用来传递数据的一个数据结构。

通道可用于两个 goroutine 之间通过传递一个指定类型的值来同步运行和通讯。操作符 <- 用于指定通道的方向,发送或接收。如果未指定方向,则为双向通道。

Go 遍历通道与关闭通道

Go 通过 range 关键字来实现遍历读取到的数据,类似于与数组或切片。格式如下:

v, ok := <-ch

如果通道接收不到数据后 ok 就为 false,这时通道就可以使用 close() 函数来关闭。一般来说,都是channel的接收端被当成range使用,然后channel的发送方被另一个协程发送数据,当没有数据发送了,发送方就close这个channel。

func fibonacci(n int, c chan int) {  
        x, y := 01  
        for i := 0; i < n; i++ {  
                c <- x  
                x, y = y, x+y  
        }  
        close(c)  
}  
  
func main() {  
        c := make(chan int10)  
        go fibonacci(cap(c), c)  
        // range 函数遍历每个从通道接收到的数据,因为 c 在发送完 10 个  
        // 数据之后就关闭了通道,所以这里我们 range 函数在接收到 10 个数据  
        // 之后就结束了。如果上面的 c 通道不关闭,那么 range 函数就不  
        // 会结束,从而在接收第 11 个数据的时候就阻塞了。  
        for i := range c {  
                fmt.Println(i)  
        }  
}

(11)Go 并发

Go 语言支持并发,我们只需要通过 go 关键字来开启 goroutine 即可。

goroutine 是轻量级线程(用户态线程也叫协程),goroutine 的调度是由 Golang 运行时进行管理的。

  • 有一个经典的例子,证明:协程也存在父子关系
func main() {
    for i := 0; i < 10; i++ {
       val := i
       go func() {  // 函数闭包
          fmt.Println(val)
       }()
    }

    time.Sleep(1000)  // 如果注释掉这句话,将不会看到有print输出,因为当子协程还没有开始运行时,父协程就已经G了。
}