2022/02/02
package main
import (
"fmt"
"math/rand"
"time"
)
func test(arr *[3]int) {
(*arr)[0] = 123
}
func test1(arr [4]int) {
}
//1.数组
//1.1数组是存放多个同一类型数据 数组是go中是值类型,指向一个数据空间
func main() {
//1.1.1定义一个数组
//数组必须规定长度 否则就是切片
// var 数组名 [数组大小]数据类型
var hens [6]float64
fmt.Println("hens=", hens)
fmt.Printf("%p\n", &hens)
//数组首元素地址可以通过数组名获取(同C语言)
//给数组每个元素赋值
hens[0] = 3.0
hens[1] = 5.0
hens[2] = 1.0
hens[3] = 3.4
hens[4] = 2.0
hens[5] = 50.0
//1.1.2四种初始化数组的方式
var arr1 [3]int = [3]int{1, 2, 3}
var arr2 = [3]int{4, 5, 6}
var arr3 = [...]int{7, 8, 9, 10}
//指定元素值对应的下标
var arr4 = [3]string{1: "tom", 0: "levi", 2: "joy"}
var arr5 = [3]string{1: "tom~", 0: "levi~", 2: "joy~"}
fmt.Println("arr1=", arr1)
fmt.Println("arr2=", arr2)
fmt.Println("arr3=", arr3)
fmt.Println("arr4=", arr4)
fmt.Println("arr5=", arr5)
//1.1.3遍历数组
var totalWeight float64
//1.1.3.1 常规遍历
for i := 0; i < len(hens); i++ {
totalWeight += hens[i]
}
//1.1.3.2 for-range 遍历
//基本语法
// for index , value := range array{
// }
//说明
//--> 第一个返回值index是数组的下标
//--> 第一个返回值value是在该下表位置的值
//--> 他们都仅在for循环内部可见的局部变量
//--> 遍历数组元素时,如果不想使用下标index,可以使用占位符下划线 _ 标记
//--> index和value是不固定的,但是一般推荐使用
for _, value := range hens {
fmt.Println(value)
}
for index, value := range hens {
fmt.Printf("%d只鸡的体重是%.2f\n", index, value)
}
avgWeight := totalWeight / float64(len(hens)) //go内运算需要保持数据类型一致
//如果除数是常数则没问题 如果是变量必须要数据类型一致
fmt.Printf("总体重是: %v, 平均体重是: %.2f\n", totalWeight, avgWeight)
//1.2数组是多个相同类型数据的组合,一个数组一旦声明/定义了,长度是固定的,不能动态变化
var arr01 [3]int
arr01[0] = 1
arr01[1] = 2
arr01[2] = 3
//arr01[2] = 1.1 //报错,类型不匹配
//arr01[3] = 3 //长度固定,不能动态变化,否则报越界(数组不能动态增长)
//1.3 数组创建后,如果没有赋值,会有初始值
// 数值(整数系列,浮点数系列) -> 0
// 字符串 -> ""
// bool -> false
//1.4 go的数组属于值类型,在默认情况下是值传递,因此会进行值拷贝,数组间不会相互影响
//1.5 数组引用传递
test(&[3]int{11, 22, 33})
//1.6 在go中长度是数组类型的一部分,在传递函数参数时,需要考虑数组的长度
var arr22 = [...]int{1, 2, 3, 4}
test1(arr22)
var byteArr [26]byte
for i := 0; i < len(byteArr); i++ {
byteArr[i] = 'A' + byte(i)
}
fmt.Println("byteArr = ", byteArr)
for i := 0; i < len(byteArr); i++ {
fmt.Printf("%c\n", byteArr[i])
}
//用for-range求和 求平均值
var array [5]int = [...]int{1, -1, 9, 90, 12}
sum := 0
for _, value := range array {
sum += value
}
fmt.Printf("sum = %d, avg = %d\n", sum, sum/len(array))
//如何让平均值保留到小数点
fmt.Printf("sum = %d, avg = %f\n", sum, float64(sum)/float64(len(array)))
//1.7 随机生成五个数,并将其反转打印
//rand.Intn()生成随机数
//得到随机数,就放到一个int数组
//反转打印, 可以直接倒叙打印, 也可以交换数据元素
var intArr [5]int
rand.Seed(time.Now().UnixNano())
for i := 0; i < len(intArr); i++ {
intArr[i] = rand.Intn(1000)
}
fmt.Println("intArr1 = ", intArr)
temp := 0
for i := 0; i < len(intArr)/2; i++ {
temp = intArr[len(intArr)-1-i]
intArr[len(intArr)-1-i] = intArr[i]
intArr[i] = temp
}
fmt.Println("intArr2 = ", intArr)
}
package main
import "fmt"
//2.切片
//***2.1 切片的基本介绍
// 2.1.1 切片是数组的引用
// 2.1.2 切片是数组的一个引用,因此切片是引用类型,在进行传递时,遵守引用传递的机制
// 2.1.3 切片的使用和数组类似,遍历切片,访问切片的元素和求切片长度len都一样
// 2.1.4 切片的长度是可以变化的,因此切片是一个可以动态变化的数组
// 2.1.5 切片定义的基本语法
// var 变量名 []类型
// 比如: var a[] int
func main() {
//***2.2 切片的基本使用
var intArr [5]int = [...]int{1, 2, 3, 4, 5}
//声明/定义 一个切片
slice := intArr[1:3]
//[p1:p2] p1不写默认值是0, p2不写默认值是数组最大长度, 也可以两个都不写
slice5 := intArr[0:5]
fmt.Println("slice5 = ", slice5) //output: slice5 = [1 2 3 4 5]
//等价
slice6 := intArr[:5]
fmt.Println("slice6 = ", slice6) //output: slice6 = [1 2 3 4 5]
slice7 := intArr[0:len(intArr)]
fmt.Println("slice7 = ", slice7) //output: slice7 = [1 2 3 4 5]
//等价
slice8 := intArr[0:]
fmt.Println("slice8 = ", slice8) //output: slice8 = [1 2 3 4 5]
//2.2.1 slice 就是切片名
//2.2.2 intArr[1:3] 表示slice引用到intArr这个数组的下标为1的元素到下标为3的元素,但是不包含3
// 起始下标为1, 终止下标为3, 但是不包含下标3
fmt.Println("intArr = ", intArr)
fmt.Println("slice = ", slice)
fmt.Println("slice 元素个数是 ", len(slice))
fmt.Println("slice 元素内容是 ", slice)
//切片的容量是可以动态变化的 , 一般是长度的两倍,但是不一定
fmt.Println("slice 容量是 ", cap(slice))
//***2.3 slice的底层是一个结构体,三个部分组成
//1. 存放引用的首个元素的地址
//2. 存放切片的长度
//3. 存放切片的容量
//***2.4 切片的基本使用
//2.4.1 定义一个切片,然后让切片去引用一个已经创建好的数组,比如前面2.2的案例
//2.4.2 通过make来创建切片
// 基本语法
// var 切片名[]type = make([]type, len, [cap])
//make参数说明: type: 数据类型 len: 大小 cap: 指定切片容量,可选
//如果分配了cap,则cap 必须大于或等于 len
var slice1 []float64
fmt.Println(slice1) // output: []
var slice2 []float64 = make([]float64, 5, 10)
fmt.Println(slice2) // output: [0 0 0 0 0]
slice2[1] = 1
slice2[2] = 3
fmt.Println(slice2) // output: [0 1 3 0 0]
fmt.Println(len(slice2)) // output: 5
fmt.Println(cap(slice2)) // output: 10
//通过make方式创建切片可以指定切片的大小和容量
//如果没有给切片的各个元素赋值,那么使用默认值,0, "", false
//通过make方式创建的切片对应的数组是由make底层维护,对外不可见,只能通过slice去访问各个元素
//2.4.3 定义一个切片,直接就指定具体数组,使用原理类似make的方式
var slice3 []int = []int{1, 2, 3, 4, 5}
fmt.Println("slice3 = ", slice3)
fmt.Println("slice3 len = ", len(slice3))
fmt.Println("slice3 cap = ", cap(slice3))
//方式一和方式二的区别
//方式一是直接引用数组,这个数组是事先存在的,程序员是可见的
//方式二是通过make来创建切片,make也会创建一个数组,是由切片在底层进行维护,程序员是看不见的
//***2.5 切片的遍历
//2.5.1 for常规遍历切片
var numArr [5]int = [...]int{1, 2, 3, 4, 5}
slice4 := numArr[1:4]
for i := 0; i < len(slice4); i++ {
fmt.Printf("slice[%v] = %v\n", i, slice4[i])
//output:
/*
slice[0] = 2
slice[1] = 3
slice[2] = 4
*/
}
fmt.Println()
//2.5.2 for-range遍历切片
for i, value := range slice4 {
fmt.Printf("slice[%v] = %v\n", i, value)
//output:
/*
slice[0] = 2
slice[1] = 3
slice[2] = 4
*/
}
//***2.6 切片可以继续切片
//操作和切数组相同
//***2.7 切片可以使用append函数进行动态增加
var sli1 []int = []int{1, 2, 3}
fmt.Println("sli1 = ", sli1)
//2.7.1 通过append直接给sli1追加具体的元素,但是元素类型要一致
sli2 := append(sli1, 400, 500, 600)
fmt.Println("sli2 = ", sli2)
//2.7.2 追加过后 sli1不会变化,会拷贝到sli2
//如果要变化本身
sli2 = append(sli2, 111, 11, 1222)
fmt.Println("sli2 = ", sli2)
//2.7.3 也可以直接追加切片
sli2 = append(sli2, sli2...) //...是固定的写法
fmt.Println("sli2 = ", sli2)
//***2.8 切片append的底层分析细节
//2.8.1 切片append操作的本质就是对数组扩容
//2.8.2 go底层会创建一个新的数组newArr(安装扩容后大小)
//2.8.3 将slice原来包含的元素拷贝到新的数组newArr
//2.8.4 slice重新引用到newArr
//2.8.5 注意newArr是在底层来维护的,程序员不可见
var t1 [5]int = [5]int{1, 2, 3, 4, 5}
var s1 []int = t1[2:5] //3 4 5
var s2 []int = s1[0:2]
s2[0] = 100
fmt.Println("t1 = ", t1) //output: t1 = [1 2 100 4 5]
fmt.Println("s1 = ", s1) //output: s1 = [100 4 5]
fmt.Println("s2 = ", s2) //output: s2 = [100 4]
//***2.9 切片拷贝
var s3 []int = []int{1, 2, 3, 4, 5}
var s4 = make([]int, 10)
copy(s4, s3)
fmt.Println("s3 = ", s3) //output: s3 = [1 2 3 4 5]
fmt.Println("s4 = ", s4) //output: s4 = [1 2 3 4 5 0 0 0 0 0]
s4[0] = 100
fmt.Println("s3 = ", s3) //output: s3 = [1 2 3 4 5]
fmt.Println("s4 = ", s4) //output: s4 = [100 2 3 4 5 0 0 0 0 0]
//s3 和 s4的数据空间是独立的
var s5 []int = []int{1, 2, 3, 4, 5}
var s6 = make([]int, 3)
copy(s6, s5)
fmt.Println("s6 = ", s6) //output: s6 = [1 2 3]
//如果内存小于被拷贝的切片,那就拷贝满即可
}
package main
import "fmt"
//string 和 slice
func fbn(n int) []uint64 {
//1.声明一个切片,切片大小是n
var fbnSlice []uint64 = make([]uint64, n)
fbnSlice[0] = 1
fbnSlice[1] = 1
for i := 2; i < n; i++ {
fbnSlice[i] = fbnSlice[i-1] + fbnSlice[i-2]
}
return fbnSlice
}
func main() {
//1.string底层是一个byte数组,因此string也可以进行切片处理
var myStr string = "hello world"
//使用切片获取world
slice := myStr[6:]
fmt.Println("slice =", slice)
//2.string是不可变的,也就说不能通过string[0]='z'方式来修改字符串
//myStr[2] = 'q' //error
//3.如果需要修改字符串,可以先将string->[]byte 或者 []rune->修改->重写转成string
//把 h 改为-> z
//1.这个方法可以处理英文和数字,不能处理中文,[]byte是按字节来处理,汉字是三个字节
arr1 := []byte(myStr)
arr1[0] = 'z'
myStr = string(arr1)
fmt.Println("myStr1 =", myStr)
//2.这个方法可以处理中文,因为[]rune是按照字符来处理的
arr2 := []rune(myStr)
arr2[0] = '中'
myStr = string(arr2)
fmt.Println("myStr2 =", myStr)
//编写函数接收一个 n int 能够将斐波那契的数列放到切片中
//思路
//1.声明一个函数 fbn(n int)([]uint64)
//2.编写fbn(n int) 进行for循环来存放斐波那契数列 0-> 1 1-> 1
fbnSliceRet := fbn(10)
fmt.Println("fbnSliceRet =", fbnSliceRet)
}