一.闭包和匿名函数
1.1 闭包的定义
函数可以嵌套定义(嵌套的函数一般为匿名函数),即在一个函数内部可以定义另一个函数。Go语言通过匿名函数支持闭包,C++不支持匿名函数,在C++11中通过Lambda表达式支持闭包。闭包是由函数及其相关引用环境组合而成的实体(即:闭包=函数+引用环境)。闭包只是在形式和表现上像函数,但实际上不是函数。函数是一些可执行的代码,函数代码在函数被定义后就确定,不会在执行时发生变化,所以一个函数只有一个实例。闭包在运行时可以有多个实例,不同的引用环境和相同的函数组合可以产生不同的实例。
所谓引用环境是指在程序执行中的某个点所有处于活跃状态的约束所组成的集合。约束是指一个变量的名字和其所代表的对象之间的联系。由于在支持嵌套作用域的语言中,有时不能简单直接地确定函数的引用环境,因此需要将引用环境与函数组合起来。
1.2 闭包的本质
闭包是包含自由变量的代码块,变量不在代码块内或者任何全局上下文中定义,而是在定义代码块的环境中定义。由于自由变量包含在代码块中,所以只要闭包还被使用,那么自由变量以及引用的对象就不会被释放,要执行的代码为自由变量提供绑定的计算环境>闭包可以作为函数对象或者匿名函数。支持闭包的多数语言都将函数作为第一级对象,即函数可以存储到变量中作为参数传递给其它函数,能够被函数动态创建和返回。
1.3 闭包的理解、
闭包将函数内部和函数外部连接起来的桥梁,能够读取其他函数内部变量的函数。可以让这些函数内的变量始终保持在内存中,不会在函数被调用后被释放。
package main
import "fmt"
func getSequence() func() int {
i:=0
return func() int {
i+=1
return i
}
}
func main(){
//nextNumber 为一个函数,函数 i 为 0
nextNumber := getSequence()
fmt.Println(nextNumber())
fmt.Println(nextNumber())
fmt.Println(nextNumber())
nextNumber1 := getSequence()
fmt.Println(nextNumber1())
fmt.Println(nextNumber1())
}
输出结果是:
1
2
3
1
2
二.数组和切片
2.1 数组
- 数组:是同一种数据类型的固定长度的序列。
- 数组定义:var a [len]int,比如:var a[5]int。(一旦定义,长度不能变)
- 长度是数组类型的一部分,因此,var a[5] int和var a[10]int是不同的类型。
- 数组可以通过下标进行访问,下标是从0开始,最后一个元素下标是:len-1。
- 访问越界,如果下标在数组合法范围之外,则触发访问越界,会panic。
- 数组是值类型,因此改变副本的值,不会改变本身的值。
package main
import (
"fmt"
)
func modify(arr [5]int) {
arr[0] = 100
return
}
func main() {
var a [5]int
modify(a)
for i := 0; i < len(a); i++ {
fmt.Println(a[i])
}
}
2.1.1 数组的初始化
var age0 [5]int = [5]int{1,2,3}
var age1 = [5]int{1,2,3,4,5}
var age2 = […]int{1,2,3,4,5,6}
var str = [5]string{3:”hello world”, 4:”tom”}
2.1.2 多维数组
var age [5][3]int
var f [2][3]int = [...][3]int{{1, 2, 3}, {7, 8, 9}}
2.1.3 多维数组的遍历
package main
import (
"fmt"
)
func main() {
var f [2][3]int = [...][3]int{{1, 2, 3}, {7, 8, 9}}
for k1, v1 := range f {
for k2, v2 := range v1 {
fmt.Printf("(%d,%d)=%d ", k1, k2, v2)
}
fmt.Println()
}
}
2.2 切片
2.2.1 切片的使用
切片初始化:var slice []int = arr[start:end] (包含start到end之间的元素,但不包含end)
Var slice []int = arr[0:end]可以简写为 var slice []int=arr[:end]
Var slice []int = arr[start:len(arr)] 可以简写为 var slice[]int = arr[start:]
Var slice []int = arr[0, len(arr)] 可以简写为 var slice[]int = arr[:]
- 通过make来创建切片:
var slice []type = make([]type, len)
slice := make([]type, len)
slice := make([]type, len, cap)
- 用append内置函数操作切片:
var a = []int{1,2,3}
var b = []int{4,5,6}
a = append(a, b…)
- for range 遍历切片:
for index, val := range slice {
}
- 切片拷贝:
s1 := []int{1,2,3,4,5}
s2 := make([]int, 10)
copy(s2, s1)
s3 := []int{1,2,3}
s3 = append(s3, s2…)
s3 = append(s3, 4,5,6)
- string与slice:
string底层就是一个byte的数组,因此,也可以进行切片操作。
str := “hello world”
s1 := str[0:5]
fmt.Println(s1)
s2 := str[5:]
fmt.Println(s2)
2.2.3 切片的原理
- 切片是数组的一个引用,因此切片是引用类型。
- 切片的长度可以改变,因此,切片是一个可变的数组。
- 切片遍历方式和数组一样,可以用len()求长度。
- cap可以求出slice最大的容量,0 <= len(slice) <= (array),其中array是slice引用的数组。
- 切片的定义:var 变量名 []类型,比如 var str []string var arr []int。