go-数组 切片
数组
- 数组可以存放多个同一类型数据,数组也是一种数据类型,在go中属于值类型
func main() {
chicken := [6]float32{3, 5, 5.3, 2.1, 2.6, 6.3}
var sum float32 = 0
for _, temp := range chicken {
sum += temp
}
fmt.Printf("sum: %v\n", sum) // sum: 24.3
fmt.Printf("avg: %v\n", sum/float32(len(chicken))) // avg: 4.0499997
}
数组定义和内存布局
- 数组的地址可以通过数组名来获取&arr
- 数组的第一个元素的地址,就是数组的首地址(也不一定)
- 数组各个元素的地址间隔是依据数组的类型决定的,比如:int64是 -->8个字节
func main() {
// 定义方式1
var arr1 [3]int
// arr1的地址: 0xc00012c018 arr1[0]:0xc00012c018 arr1[1]:0xc00012c020
fmt.Printf("arr1的地址: %p\t arr1[0]:%p\t arr1[1]:%p\t \n", &arr1, &arr1[0], &arr1[1])
fmt.Printf("arr1: %v\n", arr1) // arr1: [0 0 0]
var arr4 []int
fmt.Printf("arr4: %v\n", arr4) // arr4: []
// 定义方式2
var arr2 = [5]int{1, 2, 3}
fmt.Printf("arr2: %v\n", arr2) // arr2: [1 2 3 0 0]
var arr5 = []int{1, 2, 3}
fmt.Printf("arr5: %v\n", arr5) // arr5: [1 2 3]
// 定义方式3
arr3 := []int{1, 2, 3, 4}
fmt.Printf("arr3: %v\n", arr3) // arr3: [1 2 3 4]
// arr3的地址: 0xc00011c030 arr3[0]:0xc000130020 arr3[1]:0xc000130028
fmt.Printf("arr3的地址: %p\t arr3[0]:%p\t arr3[1]:%p\t \n", &arr3, &arr3[0], &arr3[1])
}
数组的使用
func main() {
var score [5]float32
for i := 0; i < 5; i++ {
fmt.Println("请输入score : ")
fmt.Scanln(&score[i])
}
fmt.Printf("score: %v\n", score)
// 四种初始化数组方式
var numArr1 [3]int = [3]int{1, 2, 3}
fmt.Printf("numArr1: %v\n", numArr1) // numArr1: [1 2 3]
var numArr2 = [3]int{3, 4, 5}
fmt.Printf("numArr2: %v\n", numArr2) // numArr2: [3 4 5]
var numArr3 = [...]int{5, 6, 7}
fmt.Printf("numArr3: %v\n", numArr3) // numArr3: [5 6 7]
var numArr4 = [...]int{1: 800, 0: 900, 3: 999} // numArr4: [900 800 0 999]
fmt.Printf("numArr4: %v\n", numArr4)
strArr5 := [...]string{1: "tom", 3: "jack", 2: "wxy"}
fmt.Printf("strArr5: %q\n", strArr5) // strArr5: ["" "tom" "wxy" "jack"]
}
数组for-range遍历
- 第一返回值index是数组的下标
- 第二个value是该下标位置的值
- 他们都是仅在for循环内部可见的局部变量
- 遍历数组元素时,若不想使用下标index,可以用占位符_代替
- index和value的名称不是固定的,可以自定义
func main() {
var nums = [5]int{1, 2, 3, 4, 5}
fmt.Printf("nums: %v\n", nums)
for i := 0; i < len(nums); i++ {
fmt.Printf("%v\t", nums[i]) // 1 2 3 4 5
}
fmt.Println("")
for index, v := range nums {
fmt.Printf("v%v: %v\t", index, v) // v0: 1 v1: 2 v2: 3 v3: 4 v4: 5
}
fmt.Println("")
for _, v := range nums {
fmt.Printf("v: %v\t", v) // v: 1 v: 2 v: 3 v: 4 v: 5
}
fmt.Println("")
}
注意事项
-
数组是多个相同类型数据的组合,一个数组一旦声明/定义了,其长度是固定的,不能动态变化
-
var arr []int,这时arr就是一个slice切片
-
数组中的元素可以是任何数据类型,包括值类型和引用类型,但是不能混用
-
数组创建后,若没有赋值,有默认值
数值类型数组 默认值为0
字符串数字 默认值为“”
bool数组 默认值为 false
-
使用数组的步骤 ① 声明数组并开辟空间 ② 给数组各个元素赋值 ③ 使用数组
-
数组的下标是从0开始的
-
数组下标必须在指定范围内使用,否则会报panic:数组越界
-
go的数组属于值类型,在默认情况下是值传递,因此会进行值拷贝
-
若想在其他函数中,去修改原来的数组,可以用引用传递方式(指针方式)
-
若不指定长度就是切片slice,若指定长度就是数组
-
长度是数组类型的一部分,在传递函数参数时,需要考虑数组的长度
func test1(arr [3]int) { // 数组是值传递, 长度要写上
arr[1] = 10
}
func test2(arr []int) { // 切片是引用传递
arr[1] = 10
}
func test3(arr *[3]int) { // 引用指针传递
(*arr)[1] = 20
}
func main() {
var arr [3]int = [3]int{1, 2, 3} // 数组
test1(arr)
fmt.Printf("arr: %v\n", arr) // arr: [1 2 3]
var arr2 []int = []int{1, 2, 3, 4, 5} // 切片
test2(arr2)
fmt.Printf("arr2: %v\n", arr2) // arr2: [1 10 3 4 5]
var arr3 = [3]int{1, 2, 3}
test3(&arr3) // 引用指针传递
fmt.Printf("arr3: %v\n", arr3) // arr3: [1 20 3]
}
实例
func main() {
// 1. 创建一个byte类型的26个元素的数组,存放A-Z
var char [26]byte
for i := 0; i < 26; i++ {
char[i] = 'A' + byte(i)
}
// char: [A B C D E F G H I J K L M N O P Q R S T U V W X Y Z]
fmt.Printf("char: %c\n", char)
// 2. 求出一个数组的最大值及其下标
var arr [5]float64 = [...]float64{1.5, 3.6, 10.5, 6.3, 7.2}
var maxNum float64 = 0
var maxNumIndex int = 0
for index, value := range arr {
if maxNum < value {
maxNum = value
maxNumIndex = index
}
}
fmt.Printf("maxNum: %v\n", maxNum) // maxNum: 10.5
fmt.Printf("maxNumIndex: %v\n", maxNumIndex) // maxNumIndex: 2
// 3. 求数组的总和 平平均值
var sum float64 = 0
var avg float64 = 0
for _, value := range arr {
sum += value
}
avg = sum / float64(len(arr))
fmt.Printf("sum: %v\n", sum) // sum: 29.099999999999998 存在错误
fmt.Printf("avg: %v\n", avg) // avg: 5.819999999999999 存在错误
// 4. 数组反转
var arrNum [5]int
for i := 0; i < 5; i++ {
rand.Seed(time.Now().UnixNano())
arrNum[i] = rand.Intn(20)
}
fmt.Printf("arrNum: %v\n", arrNum)
for i, j := 0, len(arrNum)-1; i < j; i++ {
temp := arrNum[i]
arrNum[i] = arrNum[j]
arrNum[j] = temp
j--
}
fmt.Printf("arrNum: %v\n", arrNum)
}
切片
-
切片的英文是slice
-
切片是数组的一个引用,因此切片是引用类型,在进行传递时,遵守引用传递的机制
-
切片的使用和数组类似,遍历切片、访问切片的元素和求切片的长度len(slice)都一样
-
切片的长度是可以变化的,因此切片是一个可以动态变化的数组
-
切片的定义基本语法:
var 变量名 []数据类型
func main() {
var intArr [5]int = [5]int{1, 2, 3, 4, 5}
// 引用intArr数组的下标区间[1,3)的值 1是起始下标,3是终止下标,不包含3
sliceArr := intArr[1:3]
fmt.Printf("sliceArr: %v\n", sliceArr) // sliceArr: [2 3]
fmt.Println("len: ", len(sliceArr)) // len: 2
fmt.Println("cap: ", cap(sliceArr)) // cap: 4
/*
func cap(v Type) int
内建函数cap返回 v 的容量,这取决于具体类型:
数组:v中元素的数量,与 len(v) 相同
数组指针:*v中元素的数量,与len(v) 相同
切片:切片的容量(底层数组的长度);若 v为nil,cap(v) 即为零
信道:按照元素的单元,相应信道缓存的容量;若v为nil,cap(v)即为零
func append(slice []Type, elems ...Type) []Type
内建函数append将元素追加到切片的末尾。若它有足够的容量,其目标就会重新切片以容纳新的元素。
否则,就会分配一个新的基本数组。append返回更新后的切片,因此必须存储追加后的结果。
slice = append(slice, elem1, elem2)
slice = append(slice, anotherSlice...)
slice = append([]byte("hello "), "world"...)
*/
var slice []int = []int{1, 2, 3, 4, 5}
slice = append(slice, int(6), int(7))
fmt.Printf("slice: %v\n", slice) // slice: [1 2 3 4 5 6 7]
fmt.Printf("len(slice): %v\n", len(slice)) // len(slice): 7
fmt.Printf("cap(slice): %v\n", cap(slice)) // cap(slice): 10
slice = append(slice, sliceArr...)
fmt.Printf("slice: %v\n", slice) // slice: [1 2 3 4 5 6 7 2 3]
fmt.Printf("len(slice): %v\n", len(slice)) // len(slice): 9
fmt.Printf("cap(slice): %v\n", cap(slice)) // cap(slice): 10
}
切片的内存布局
-
slice的确是一个引用类型
-
slice从底层来说,其实是一个数据结构,(struct 结构体)
Type slice struct {
ptr *[2]int
len int
cap int
}
func main() {
var intArr [5]int = [5]int{1, 2, 3, 4, 5}
// 引用intArr数组的下标区间[1,3)的值 1是起始下标,3是终止下标,不包含3
sliceArr := intArr[1:3]
fmt.Printf("sliceArr: %v\n", sliceArr) // sliceArr: [2 3]
fmt.Println("len: ", len(sliceArr)) // len: 2
fmt.Println("cap: ", cap(sliceArr)) // cap: 4
fmt.Printf("&sliceArr: %p\n", &sliceArr) // &sliceArr: 0xc0000a4018
fmt.Printf("&sliceArr[0]: %p\n", &sliceArr[0]) // &sliceArr[0]: 0xc0000aa038 存放地址
fmt.Printf("&intArr[1]: %v\n", &intArr[1]) // &intArr[1]: 0xc0000aa038 本身地址
sliceArr[0] = 10
fmt.Printf("sliceArr: %v\n", sliceArr) // sliceArr: [10 3] 改变
fmt.Printf("intArr: %v\n", intArr) // intArr: [1 10 3 4 5] 跟着改变
/*
func cap(v Type) int
内建函数cap返回 v 的容量,这取决于具体类型:
数组:v中元素的数量,与 len(v) 相同
数组指针:*v中元素的数量,与len(v) 相同
切片:切片的容量(底层数组的长度);若 v为nil,cap(v) 即为零
信道:按照元素的单元,相应信道缓存的容量;若v为nil,cap(v)即为零
func append(slice []Type, elems ...Type) []Type
内建函数append将元素追加到切片的末尾。若它有足够的容量,其目标就会重新切片以容纳新的元素。
否则,就会分配一个新的基本数组。append返回更新后的切片,因此必须存储追加后的结果。
slice = append(slice, elem1, elem2)
slice = append(slice, anotherSlice...)
slice = append([]byte("hello "), "world"...)
*/
var slice []int = []int{1, 2, 3, 4, 5}
slice = append(slice, int(6), int(7)) // 在后面添加m个元素
fmt.Printf("slice: %v\n", slice) // slice: [1 2 3 4 5 6 7]
fmt.Printf("len(slice): %v\n", len(slice)) // len(slice): 7
fmt.Printf("cap(slice): %v\n", cap(slice)) // cap(slice): 10
slice = append(slice, sliceArr...) // 在后面添加slice
fmt.Printf("slice: %v\n", slice) // slice: [1 2 3 4 5 6 7 2 3]
fmt.Printf("len(slice): %v\n", len(slice)) // len(slice): 9
fmt.Printf("cap(slice): %v\n", cap(slice)) // cap(slice): 10
fmt.Printf("&slice: %p\n", &slice) // &slice: 0xc00000c048
fmt.Printf("&slice[0]: %p\n", &slice[0]) // &slice[0]: 0xc00001e050
fmt.Printf("&slice[1]: %p\n", &slice[1]) // &slice[1]: 0xc00001e058 与上面相差8个字节
fmt.Printf("slice[1]: %v\n", slice[1]) // slice[1]: 2
}
切片的三种方式
-
定义一个切片,然后让切片去引用一个已经创建好的数组
-
通过make来创建切片
var 切片名 []type = make([]type, len, cap)
参数说明:type是数据类型
len是大小
cap是指定切片容量,可选,若分配了cap,则需要cap >= len
-
定义一个切片,直接指定具体数组,使用原理类似make方式
func main() {
// 1. 引用一个数组 指向所引向的地址空间 通过数组也能访问切片
var arr [5]int = [5]int{1, 2, 3, 5, 7}
var slice1 []int = arr[0:5]
slice2 := arr[0:5]
fmt.Printf("slice1: %v\n", slice1) // slice1: [1 2 3 5 7]
fmt.Printf("slice2: %v\n", slice2) // slice2: [1 2 3 5 7]
// 2. make 自己开辟一个地址空间
var slice3 []int = make([]int, 5, 10)
fmt.Printf("slice3: %v\n", slice3) // slice3: [0 0 0 0 0]
slice3 = []int{3: 33}
fmt.Printf("slice3: %v\n", slice3) // slice3: [0 0 0 33]
slice3[1] = 11
slice3[0] = 22
slice3[3] = 55
fmt.Printf("slice3: %v\n", slice3) // slice3: [22 11 0 55]
fmt.Printf("len(slice3): %v\n", len(slice3)) // len(slice3): 4
fmt.Printf("cap(slice3): %v\n", cap(slice3)) // cap(slice3): 4
// (1) 通过make方式创建切片可以指定切片的大小和容量
// (2) 若没有给切片的各个元素赋值,那么就会使用默认值int float ... 0 0
// (3) 通过make方式创建的切片对应的数组是由make底层维护,对外不可见,即只能通过slice去访问各个元素
// 3. 直接指定具体数组
var slice4 []int = []int{11, 33, 55, 66}
fmt.Printf("slice4: %v\n", slice4) // slice4: [11 33 55 66]
fmt.Printf("len(slice4): %v\n", len(slice4)) // len(slice4): 4
fmt.Printf("cap(slice4): %v\n", cap(slice4)) // cap(slice4): 4
}
切片的遍历
func main() {
var slice []int = []int{1, 2, 3, 4, 5}
// 1. 遍历方式for
// slice[i]: 1 slice[i]: 2 slice[i]: 3 slice[i]: 4 slice[i]: 5
for i := 0; i < len(slice); i++ {
fmt.Printf("slice[i]: %v\t", slice[i])
}
fmt.Println("")
// 2. 遍历方式for-range
// value0: 1 value1: 2 value2: 3 value3: 4 value4: 5
for index, value := range slice { // index 可以用 占位符 代替
fmt.Printf("value%v: %v\t", index, value)
}
fmt.Println("")
}
注意事项
-
切片初始化时,
var slice = arr[startIndex : endIndex]
从arr数组下标为startIndex,取到下标为endIndex的元素(不含arr[endIndex])
var slice = arr[0:end]
可以简写成var slice = arr[:end]
var slice = arr[start:len(arr)]
可以简写成var slice = arr[start:]
var slice = arr[0:len(arr)]
可以简写成var slice = arr[:]
-
cap是内置函数,用于统计切片的容量,即最大可以存放多少个元素
-
切片定义后,还不能使用,因为本身是一个空的,需要让其引用到一个数组,或者make一个空间供切片来使用
-
切片可继续切片
var slice2 = slice1[1:3]
-
append内置函数的使用
- 切片append操作的本质就是对数组扩容
- go底层会创建一下新的数组newArr(安装扩容后大小)
- 将slice原来包含的元素拷贝到新的数组newArr
- slice重新引用到newArr
- 注意:newArr是在底层来维护的,程序员不可见
- copy内置函数
func main() {
// 1 - 4 案例
var arr [5]int = [...]int{1, 2, 3, 4, 5}
var slice1 []int = arr[2:]
var slice2 = slice1[:]
fmt.Printf("slice1: %v\n", slice1) // slice1: [3 4 5]
fmt.Printf("slice2: %v\n", slice2) // slice2: [3 4 5]
slice2[1] = 999
fmt.Printf("arr: %v\n", arr) // arr: [1 2 3 999 5]
fmt.Printf("slice1: %v\n", slice1) // slice1: [3 999 5]
fmt.Printf("slice2: %v\n", slice2) // slice2: [3 999 5]
// 5 append
var slice []int = []int{100, 200, 300}
slice = append(slice, 400)
fmt.Printf("slice: %v\n", slice) // slice: [100 200 300 400]
slice = append(slice, 500, 600)
fmt.Printf("slice: %v\n", slice) // slice: [100 200 300 400 500 600]
slice = append(slice, slice2...) // slice: [100 200 300 400 500 600 3 999 5]
fmt.Printf("slice: %v\n", slice)
// 6. copy 拷贝, 拷贝的两个切片是相互独立,相互不影响,
// 对其中一个修改,不会影响其他的修改
var newSlice1 []int = make([]int, 5)
updateNum := copy(newSlice1, slice)
fmt.Printf("newSlice1: %v\n", newSlice1) // newSlice: [100 200 300 400 500]
fmt.Printf("updateNum: %v\n", updateNum) // updateNum: 5
var newSlice2 []int = make([]int, 15)
updateNum = copy(newSlice2, slice)
fmt.Printf("newSlice2: %v\n", newSlice2) // newSlice2: [100 200 300 400 500 600 3 999 5 0 0 0 0 0 0]
fmt.Printf("updateNum: %v\n", updateNum) // updateNum: 5
newSlice1[1] = 999999
fmt.Printf("newSlice1: %v\n", newSlice1) // newSlice1: [100 999999 300 400 500] 相互独立
fmt.Printf("slice: %v\n", slice) // slice: [100 200 300 400 500 600 3 999 5]
}
- 切片是引用类型,所以在传递时,遵守引用传递机制
string和slice
-
string底层是一个byte数组,因此string也可以进行切片处理
-
string和切片在内存的形式
-
str[1] = ‘a’ 方式来修改字符串
-
若需要修改字符串,可以先将string – > []byte 或者 []rune --> 修改 --> 重新转成 string
-
string属于值类型
func main() {
// 1. 认识
str := "hello@go"
var slice = str[5:]
fmt.Printf("slice: %v\n", slice) // slice: @go
fmt.Printf("slice Type: %T\n", slice) // slice Type: string
// 2. 修改转变
var sliceByte []byte = []byte(str)
fmt.Printf("sliceByte: %c\n", sliceByte) // sliceByte: [h e l l o @ g o]
sliceByte[1] = 'A'
fmt.Printf("str: %v\n", str) // str: hello@go 不会改变
fmt.Printf("slice: %v\n", slice) // slice: @go 不会改变
fmt.Printf("sliceByte: %c\n", sliceByte) // sliceByte: [h A l l o @ g o] 改变
str = string(sliceByte)
fmt.Printf("str: %v\n", str) // str: hAllo@go 改变
fmt.Printf("slice: %v\n", slice) // slice: @go 不会改变
fmt.Printf("sliceByte: %c\n", sliceByte) // sliceByte: [h A l l o @ g o]
// 3. 处理中文
str1 := "你好 大连 hello DL"
var sliceRune []rune = []rune(str1)
fmt.Printf("sliceRune: %c\n", sliceRune) // sliceRune: [你 好 大 连 h e l l o D L]
sliceRune[0] = '您'
str1 = string(sliceRune)
fmt.Printf("str1: %v\n", str1) // str1: 您好 大连 hello DL
fmt.Printf("sliceRune: %c\n", sliceRune) // sliceRune: [您 好 大 连 h e l l o D L]
}
练习
func main() {
var n int = 0
fmt.Print("请输入 n : ")
fmt.Scanln(&n)
slice := flib(n)
fmt.Printf("slice: %v\n", slice)
}
func flib(n int) (slice []int) {
if n == 0 {
slice = []int{1}
return
}
if n == 1 {
slice = []int{1, 1}
return
}
slice = make([]int, n+1)
slice[0] = 1
slice[1] = 1
for i := 2; i <= n; i++ {
slice[i] = slice[i-1] + slice[i-2]
}
return
}
数组排序
冒泡排序
// 冒泡排序
func bubbleSort(arr *[8]int) {
var flag bool = true
for i := len(arr); i > 0 && flag; i-- {
flag = false
for j := 1; j < i; j++ {
if arr[j] < arr[j-1] {
temp := arr[j]
arr[j] = arr[j-1]
arr[j-1] = temp
flag = true
}
}
if flag {
fmt.Printf("%v : arr: %v\n", len(arr)-i+1, arr)
}
}
}
顺序查找
// 顺序查找
func find(arr [8]int, target int) int {
for index, value := range arr {
if value == target {
return index
}
}
return -1
}
二分查找
// 二分查找
func binFind(arr [8]int, target int) int {
// 1. 排序
bubbleSort(&arr)
// 2. 查找
left := 0
right := len(arr) - 1
for left < right {
mid := (right-left)>>1 + left
if arr[mid] == target {
return mid
} else if arr[mid] > target {
right = mid - 1
} else {
left = mid + 1
}
}
return -1
}
// 二分查找-递归
func binaryFind(arr [8]int, left int, right int, target int) int {
if left > right {
return -1
}
mid := (right-left)>>1 + left
if arr[mid] > target {
mid = binaryFind(arr, left, mid-1, target)
} else if arr[mid] < target {
mid = binaryFind(arr, mid+1, right, target)
} else {
return mid
}
return mid
}
// 主函数
func main() {
var arr [8]int = [...]int{2, 99, 7, 6, 9, 1, 3, 50}
bubbleSort(&arr)
var arr1 [8]int = [...]int{2, 99, 7, 6, 9, 1, 3, 50}
index := find(arr1, 99)
if index == -1 {
fmt.Println("No Find")
} else {
fmt.Printf("Find index: %v\n", index)
}
index = binFind(arr1, 6)
if index == -1 {
fmt.Println("No BinFind")
} else {
fmt.Printf("BinFind index: %v\n", index)
}
}
二维数组
二维数组介绍
func main() {
var arr [3][5]int = [3][5]int{{1, 2, 3, 4, 5}, {6, 7, 8, 9, 10}, {11, 12, 13, 14, 15}}
fmt.Printf("arr: %v\n", arr)
for i := 0; i < len(arr); i++ {
for j := 0; j < len(arr[i]); j++ {
fmt.Printf("%v\t", arr[i][j])
}
fmt.Println("")
}
fmt.Printf("&arr[0]: %p\n", &arr[0]) // &arr[0]: 0xc000022080 相差40个字节,5 * 8 = 40
fmt.Printf("&arr[1]: %p\n", &arr[1]) // &arr[1]: 0xc0000220a8
fmt.Printf("&arr[0][0]: %p\n", &arr[0][0]) // &arr[0][0]: 0xc0000c0000 与 &arr[0] 相同
fmt.Printf("&arr[0][1]: %p\n", &arr[0][1]) // &arr[0][1]: 0xc000022088 与 &arr[0][0] 相差8个字节
// 四种初始化方式
// 1.
var arr1 [2][3]int = [2][3]int{{1, 2, 3}, {4, 5, 6}}
fmt.Printf("arr1: %v\n", arr1)
// 2.
var arr2 [2][3]int
fmt.Printf("arr2: %v\n", arr2)
// 3.
var arr3 = [...][3]int{{1, 2, 3}, {3, 4, 5}, {5, 6, 7}}
fmt.Printf("arr3: %v\n", arr3)
// 4.
var arr4 = [2][3]int{{1, 2, 3}, {1, 2, 3}}
fmt.Printf("arr4: %v\n", arr4)
// 5.
arr5 := [2][3]int{{1, 2, 3}, {1, 2, 3}}
fmt.Printf("arr5: %v\n", arr5)
// 下面是切片
arr6 := [][]int{{1, 2, 3}, {4, 5}, {11, 22, 33, 44, 55}}
fmt.Printf("arr6: %v\n", arr6)
}
二维数组遍历
func main() {
var arr = [2][3]int{{1, 2, 3}, {11, 22, 33}}
for i := 0; i < len(arr); i++ {
for j := 0; j < len(arr[i]); j++ {
fmt.Printf("arr[i][j]: %v\t", arr[i][j])
}
fmt.Println("")
}
for _, value := range arr {
for _, val := range value {
fmt.Printf("val: %v\t", val)
}
fmt.Println("")
}
}
实例
func main() {
var classScore [3][5]float64
for i := 0; i < len(classScore); i++ {
for j := 0; j < len(classScore[i]); j++ {
fmt.Printf("input class %v student %v : ", i+1, j+1)
fmt.Scanln(&classScore[i][j])
}
}
fmt.Printf("classScore: %v\n", classScore)
var classVag [3]float64
var allVag float64 = 0
for i := 0; i < len(classScore); i++ {
sum := 0.0
for j := 0; j < len(classScore[i]); j++ {
sum += classScore[i][j]
}
allVag += sum
classVag[i] = sum / float64(len(classScore[i]))
}
fmt.Printf("classVag: %v\n", classVag)
vag := allVag / float64(len(classScore)*len(classScore[0]))
fmt.Printf("allVag: %v\n", vag)
}