(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.0, 0.0, 1e-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) (int, error) // 所有的实例类都必须实现这个函数
}
type StringWriter struct {
str string
}
func (sw *StringWriter) Write(data []byte) (int, error) {
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 - 对于局部变量、传参数的方式,会采用从上到下,从左到右的方式进行初始化
- 对于全局变量来说,go编译器会采用拓扑排序的方式初始化变量,如果不存在一种合法的初始化方式,就会报错
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执行顺序
(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 := 0, 1
for i := 0; i < n; i++ {
c <- x
x, y = y, x+y
}
close(c)
}
func main() {
c := make(chan int, 10)
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了。
}