2、Golang基础--包的使用、if-else语句、循环、switch语句、数组、切片、可变函数参数、map类型

1 包的使用

// 为了便于组织代码,同一种类型的代码,写在同一个包下,便于管理
// 定义包
	-新建一个文件夹
	-内部有很多go文件
	-在每个go文件的第一行,都要声明包名,并且包名必须一致
	-在一个文件夹(同级)下只能有一个包
	-在同一个包下,变量和函数都是共享的(一个包相当于一个go文件)
	-在一个包下,不能重复定义变量和函数
	-除了main包,其他都是用来被导入使用的
	-无论是函数,还是变量,大写字母开头表示导出,可以在其他包使用
	-尽量包名就是文件夹名


// 老版本的gopath和现在的go moduls的区别
		-1.11后才支持go moduls模式,现在都用
		-如果使用go path开发,所有项目的代码,必须放在 go path路径下的 src文件夹下,否则找不到,包括包,包括下载的第三方包---》弃用了
		-使用go mod以后,现在推荐的
			-代码不需要放在go path路径下的 src文件夹下了,放在任何路径下都可以
			-项目路径下必须要有个go.mod
			-go mod init


// 把go path 修改成go mod项目
		//go env -w 修改go的环境变量
		-可以移动目录
		-go mod init 项目名
			-在项目路径下生成 go.mod,自动生成,不要去动
			-内容:
				module 项目名
				go 1.17
		-如果有第三方包,需要再执行一下
			-go mod tidy  // 会去下载第三方包

2 if-else语句

// 逻辑判断

package main

import "fmt"

// if-else的使用
// if 后跟条件,符合条件会执行,代码用{}包裹
// else if 后跟条件,符合条件会执行,代码用{}包裹
// else 不需要加条件,用{}包裹
// {}的{需要和关键字在一行

func main() {  // 大括号的 { 不能换到下一行
	number := 55
    if number >= 90 && number <= 100 { 
		fmt.Println("优秀")
	} else if number >= 60 && number <= 90 {
		fmt.Println("良好")
	} else {
		fmt.Println("不及格")
	}
}

3 循环

// go 语言中循环使用for关键字
	-基于迭代的循环
	-基于索引的循环

package main

import "fmt"

// for 循环的使用
// for 后面三段,分号隔开
// 第一段:索引的开始
// 第二段:条件
// 第三段:自增或自减

func main() {
	// 1 基于索引的循环
	// 1.1 完整版
	// 循环打印出0-9
	for i := 0; i < 10; i++ {
		fmt.Println(i)
	}

	// 1.2 省略第一段: i的作用域变大
	var i2 = 0
	for ; i2 < 10; i2++ {
		fmt.Println(i2)
	}

	// 1.3 省略第三段,自增写在循环体内部
	for i3 := 0; i3 < 10; {
		fmt.Println(i3)
		i3++
	}

	// 1.4 省略第一段和第三段,
	i4 := 0
	for ; i4 < 10; {
		fmt.Println(i4)
		i4++
	}

	// 简写为:
	for i4 < 10 {
		fmt.Println(i4)
	}
	/*
	// 死循环
	for true {
		fmt.Println("死循环1")
	}
	*/

	/*
	// 1.5 三部分全省略
	for ;;{
		fmt.Println("死循环2")
	}

	// 简写成:
	for {
		fmt.Println("死循环3")
	}
	*/

	// 2 基于迭代的循环
	var a = [3]int{1, 25, 34}
	for i5, value := range a {
		fmt.Println(i5)
		fmt.Println(value)
	}

	// 用基于索引实现
	var a6 = [3]int{1, 25, 34}
	for i6 := 0; i6 < len(a6); i6++ {
		fmt.Println(i6)
		fmt.Println(a6[i6])
	}
}

4 switch语句

// 可以优雅的替换掉if-else
package main

import "fmt"

// switch使用
func main() {

	// 1 使用方式一
	name := "cx"
	switch name {
	case "cx":
		fmt.Println("11")
	case "lqz":
		fmt.Println("12")
	}

	// 2 使用方式二:switch后不跟值、case后跟
	name2 := "cx"
	switch {
	case name2 == "cx":
		fmt.Println("21")
	case name2 == "aa":
		fmt.Println("22")
	}

	// 3 使用方式三:多条件()
	// 3.1:,表示或者
	name3 := "cx"
	switch name3 {
	case "cx", "cx2":
		fmt.Println("311")
	case "lqz", "lqz2":
		fmt.Println("312")
	}

	// 3.2:||表示或者  &&表示并且
	name32 := 10
	switch {
	case name32 == 11 || name32 == 1:
		fmt.Println("321")
	case name32 > 1 && name32 < 3:
		fmt.Println("322")
	case name32 == 1, name32 == 2, name32 == 10:
		fmt.Println("333")
	}

	// 4 使用方式4:default的使用,当所有case都不满足后执行
	name4 := "cx"
	switch name4 {
	case "cx2":
		fmt.Println("cx")
	case "lqz2":
		fmt.Println("lqz")
	default:
		fmt.Println("没匹配上")
	}

	// 5 fallthrough的使用:无条件执行下一个case  只能放在语句结尾
	name5 := "cx"
	switch name5 {
	case "cx":
		fmt.Println("cx")
		fallthrough
	case "lqz":
		fmt.Println("lqz")
	default:
		fmt.Println("没匹配上")
		// 输出:cx lqz
	}
}

5 数组

// 类型,变量,连续存储数据,数据类型是一致的,类似于python中的列表(但列表可放不同类型数据)
package main

import "fmt"

// 数组(array)
func main() {
	// 1 数组的定义,定义时,大小就固定了,后期不能修改大小
	var a [3]int = [3]int{4, 5, 6}
	fmt.Println(a)

	// 2 使用:修改 取值
	// 根据下标使用
	a[0] = 99
	fmt.Println(a)
	fmt.Println(a[0])
	//fmt.Println(a[3])  // 报错

	// 3 其他定义方式
	var a3 = [3]int{4} // 可以少(如果不足的元素值为默认值0),但不能多,否则报错
	fmt.Println(a3)

	a4 := [3]int{1, 2, 3}
	fmt.Println(a4)

	a5 := [30]int{28: 1} // 指定位置赋初值 指定索引28对应值为1、其他为默认值0
	fmt.Println(a5)

	var a6 [3]int
	fmt.Println(a6) // [0, 0, 0]

	var a7 = [...]int{3, 4, 5} // 虽然使用...初始化,但是长度也是固定的,根据值多少个确定长度
	fmt.Println(a7)            // [3 4 5]

	var a8 = [...]int{50: 99}
	fmt.Println(a8)
	fmt.Printf("%T", a8) // 长度为51

	// 4 数组长度
	b := [3]int{1, 2, 3}
	fmt.Println(len(b)) // len()不需要导入包、直接使用即可

	for i := len(b) - 1; i >= 0; i-- {
		fmt.Println(a[i])
	}

	// 5 数组的循环
	// 2.1 基于索引的循环
	for i := 0; i < len(b); i++ {
		fmt.Println(b[i])
	}

	// 2.2 反向取
	for i := len(b) - 1; i>=0; i-- {
		fmt.Println(b[i])
	}

	// 5.2 基于迭代的循环
	// range是一个关键字
	// 返回一个值是索引、返回两个值是索引和值
	for i := range b {
		fmt.Println(b[i])
	}
	
	for _, value := range b {  // 变量定义后必须使用,但是用_接收可以不使用
		fmt.Println(value)
	}

	// 6 多维数组
	var b2 [3][4]int = [3][4]int{{3, 3, 3, 3}, {4, 4, 4}, {5}}
	fmt.Println(b2)

	// 6.1 循环(两层循环)
	for _, value := range b2 {
		for _, value2 := range value {
			fmt.Println(value2)
		}
	}
}

6 切片

// 切片是由数组建立的一种方便、灵活且功能强大的包装,切片本身不拥有任何数据。它们只是对现有数组的引用(指针指向数组)
package main

import "fmt"

// 切片(slice)
func main() {
	// 1 基于数组,定义切片
	a1 := [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
	var s []int // 中括号不带任何东西,就是切片类型
	s = a1[:]   // 把数组从头到尾的引用给s
	fmt.Println(a1, s)
	fmt.Printf("a的类型是%T,值是%v", a1, a1)
	fmt.Println() // 输入空行(换行)
	fmt.Printf("s的类型是%T,值是%v", s, s)
	fmt.Println()

	// 2 使用,取值,改值
	fmt.Println(s[0]) // 1
	//fmt.Println(s[100])  // 编译不报错,执行会报错

	s[1] = 999
	fmt.Println(s) // [1 999 3 4 5 6 7 8 9 10]

	// 3 切片的变化,会影响底层数组
	a3 := [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
	var s3 []int = a3[:]
	s3[0] = 999
	fmt.Println("s3:", s3) // s3: [999 2 3 4 5 6 7 8 9 10]
	fmt.Println("a3:", a3) // a3: [999 2 3 4 5 6 7 8 9 10]

	// 4 底层数组的变化也会影响数组
	a4 := [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
	// 简写:var s4 []int = a3[:]
	var s4 = a4[:]
	s4[0] = 9999
	fmt.Println("s4:", s4) // s3: [9999 2 3 4 5 6 7 8 9 10]
	fmt.Println("a4:", a4) // a3: [9999 2 3 4 5 6 7 8 9 10]

	// 5 切片的长度(len)和容量(cap)
	// 数组有长度、并且长度不能变
	// 切片也有长度、但是长度可以变,切片有容量
	a5 := [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
	var s5 = a5[0:3] // 顾头不顾尾:索引0、1、2被引用,没有3
	fmt.Println(s5)
	fmt.Println(len(s5)) // 长度为3
	fmt.Println(cap(s5)) // 容量为10(这个切片最多能存多少值,基于底层数组来的)

	// 6 长度和容量再次研究
	a6 := [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
	s6 := a6[2:9]
	fmt.Println(s6)
	fmt.Println(len(s6)) // 7
	fmt.Println(cap(s6)) // 8  容量是底层数组决定的,但是不一定是底层数组的大小,得看切片从哪个位置开始引用数组,从切片引用数组的开始位置到结束,是容量的大小

	// 7 通过内置函数make创建切片
	// make(类型,长度,容量)
	var s7 []int = make([]int, 3, 4) // 简写:var s7 = make([]int,3,4)
	fmt.Println(len(s7))             // 3
	fmt.Println(cap(s7))             // 4

	// 定义切片并初始化
	var ss = []int{2, 3, 4}
	fmt.Println(len(ss)) // 3
	fmt.Println(cap(ss)) // 3

	// 8 追加切片
	var s8 = make([]int, 3, 4)
	fmt.Println(s8)
	fmt.Println(len(s8)) // 3
	fmt.Println(cap(s8)) // 4

	s8 = append(s8, 99)
	fmt.Println(s8)
	fmt.Println(len(s8)) // 4
	fmt.Println(cap(s8)) // 4

	// 注意:此时长度已经达到容量值,如继续追加,切片容量会翻倍、自动扩容

	s8 = append(s8, 88)
	fmt.Println(s8)
	fmt.Println(len(s8)) // 5
	fmt.Println(cap(s8)) // 8

	// 9 追加切片后,底层数组如何变化?
	a9 := [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
	s9 := a9[2:9]
	fmt.Println(s9)

	// 9.1 切片变,底层数组变
	s9[0] = 666
	fmt.Println(s9) // [666 4 5 6 7 8 9]
	fmt.Println(a9) // [1 2 666 4 5 6 7 8 9 10]

	// 9.2 数组变,切片也会变
	a9[8] = 999
	fmt.Println(a9) // [1 2 666 4 5 6 7 8 999 10]
	fmt.Println(s9) // [666 4 5 6 7 8 999]

	// 9.3 追加元素(追加到临界状态,再追加,数组就不够了),底层数组会跟着变
	// 		底层数组不够了,go语言会自动申请一个新的数组,大小为原来切片容量的2倍,把原来切片的值,复制到新数组上。
	//		此时,切片与原来数组脱离,原来数组的变化不再会影响切片了
	s9 = append(s9, 93)
	fmt.Println(s9) // [666 4 5 6 7 8 999 93]
	fmt.Println(a9) // [1 2 666 4 5 6 7 8 999 93]

	fmt.Println(len(s9)) // 8
	fmt.Println(cap(s9)) // 8
	s9 = append(s9, 94)  // 此时,切片容量不够,切片与原来数组脱离
	fmt.Println(s9)      // [666 4 5 6 7 8 999 93 94]
	fmt.Println(len(s9)) // 9
	fmt.Println(cap(s9)) // 16

	s9[0] = 10000
	fmt.Println(s9) // [10000 4 5 6 7 8 999 93 94]
	fmt.Println(a9) // [1 2 666 4 5 6 7 8 999 93] 不会再跟着变化了

	// 切片就是个寄生虫,有了新宿主,就和原来宿主没关系了

	// 10 切片的函数传递
	/*
		数组是值类型,切片是引用类型(指针)
		go语言中的参数传递是:copy传递,也就是把变量复制一份到函数中
			如果是值类型,复制一个新的值传入,修改新值不会影响原来的值
			如果是引用类型,复制这个引用传入(指向没变),修改新值会影响原来的值
	*/
	var a10 = [3]int{4, 5, 6}
	test1(a10)       // test函数定义在最下方
	fmt.Println(a10) // a10不会变 [4 5 6]

	var s10 = a10[:]
	test2(s10)
	fmt.Println(s10) // s值会变,因为s是个引用 [999 5 6]

	/* 补充:
	Python与其他语言区别:
		其他语言(如Go):分为值类型、引用类型
		Python:一切皆对象、一切皆引用
			解决方式:分为 可变类型、不可变类型
			可变类型:列表、字典
				函数中修改会影响到原来的值
			不可变类型:字符串、元组、数字
				函数中修改不会影响到原来的、如要改加global声明
	*/

	// 11 多维切片
	var s11 = [][]int{{1}, {2, 2}, {3, 3, 3}}
	fmt.Println(s11)
	fmt.Println(len(s11)) // 3
	fmt.Println(cap(s11)) // 3

	fmt.Println(len(s11[0])) // 1
	fmt.Println(cap(s11[0])) // 1

	fmt.Println(len(s11[1])) // 2
	fmt.Println(cap(s11[1])) // 2

	// 11.2 通过make初始化多维切片
	var s112 = make([][]int, 3, 4)   // 内层的切片没有初始化,只要使用内层切片,内层所有的切片都要初始化才行
	fmt.Println("s112[0]:", s112[0]) // 是个没初始化的切片 s112[0]: []
	s112[0] = make([]int, 5, 6)      // 初始化内层切片
	s112[0][0] = 99
	fmt.Println(s112[0]) // [99 0 0 0 0]
	// fmt.Println(s112[0][5])  // 报错:切片越界,虽然容量为6,但还没使用到(长度为5),就不能取
	// 解决:用append在切片末尾再加一个元素,让长度变为6即可

	// 11.3 循环多维切片
	// 基于索引
	var s113 = [][]int{{1}, {2, 2}, {3, 3, 3}}
	for i := 0; i < len(s113); i++ {
		for i2 := 0; i2 < len(s113[i]); i2++ {
			fmt.Println(s113[i][i2])
		}
	}

	// 基于迭代
	for _, v := range s113 {
		for _, v2 := range v {
			fmt.Println(v2)
		}
	}

	// 12 copy:把一个切片,复制到另一个切片上
	var a12 = [10000]int{3, 4, 5}
	fmt.Println(a12) //[3 4 5 0 0 0 0 ... ]
	s12 := a12[:3]
	fmt.Println(s12) // [3 4 5]

	// 使用s12会基于一个很大的数组,内存占用高
	// 解决:将s12 copy到另一个基于小数组的切片上
	s121 := make([]int, 3, 3)
	fmt.Println(s121) // [0 0 0]
	copy(s121, s12)
	fmt.Println(s121) // [3 4 5] ,以后都用s121操作,节约了内存

	// 12.2 俩切片一样长,可以copy,如果不一样长呢?
	s1221 := make([]int, 5, 5)
	copy(s1221, s12)
	fmt.Println(s1221) // [3 4 5 0 0]  多了的用默认值0填充

	s1222 := make([]int, 2, 2)
	copy(s1222, s12)
	fmt.Println(s1222) // [3 4]  少了的则截断
}

func test1(a [3]int) {
	a[0] = 999
	fmt.Println("test1:", a)
}

func test2(a []int) {
	a[0] = 999
	fmt.Println("test2:", a)
}

7 可变函数参数

package main

import "fmt"

// 可变长函数参数:...类型

func main() {

	test3(2, 3, 4, 5)       // [2 3 4 5]  类型:[]int
	test4("aa", "bb", "cc") // [aa bb cc] 类型:[]string

	a := [3]int{3, 4, 5}
	fmt.Println(1, 2, "aa", a) // Println函数中形参类型为:...interface{}  空接口类型,可以接收任意类型

	s := []int{1, 2, 3}
	// test3(s)  // 报错
	test3(s...) // 相当于将s打散后传入函数 结果:[1 2 3] 类型:[]int

	// 如果既想无限接收number类型、又想无限接收string类型。可以自定义类型。

}

func test3(a ...int) { // 可以接收任意长度参数、但类型必须一致
	fmt.Println(a)
	fmt.Println()
	fmt.Printf("类型:%T", a) // 切片类型 []int
}

func test4(a ...string) {
	fmt.Println(a)
	fmt.Println()
	fmt.Printf("类型:%T", a) // 切片类型 []string
	// 取到第几个参数
	fmt.Println("第一个参数:", a[0])
}


8 map

map 是在 Go 中将值(value)与键(key)关联的内置类型。通过相应的键可以获取到值(Python的字典类型)
value值可以任意,但是go中value值必须都一致
key值只能用数字,字符串,布尔,key值也固定
package main

import "fmt"

// map的使用

func main() {
	//1 map的定义
	var m map[string]int // map[key类型]value类型
	fmt.Println(m)       // 只定义,没有初始化,零值也是nil
	if m == nil {
		fmt.Println("map没有初始化")
	}
	// 第一个参数传类型,map有长度没有容量
	var m2 = make(map[string]int)
	fmt.Println(m) //map[]  已经不是nil了,它可以直接用了,因为初始化了
	if m2 == nil {
		fmt.Println("m2没有初始化")
	} else {
		fmt.Println("m2已经初始化了")
	}

	/*
		补充  数字,字符串,布尔,数组,切片,map类型的0值
		值类型:有自己的0值,
			数字:0,
			字符串:""
			布尔:false
			数组:数组里元素类型的零值
		引用类型:零值是 nil  (None:python中所有类型的空值都是None)
			切片
			map
	*/
	// 值类型验证
	var a1 float32
	var a2 string
	var a3 bool
	var a4 [4]string
	fmt.Println(a1, a2, a3, a4)

	// 引用类型验证
	var ss []int
	fmt.Println(ss) // []
	if ss == nil {
		fmt.Println("空的") // 执行
	}

	var s = make([][]int, 3, 3)
	if s[0] == nil {
		fmt.Println("没有初始化") // 执行
	}
	// s[0][0] = 99 // 报错  nil[0] 相当于Python的 None[0]

	var s1 []int
	fmt.Println(s1)
	if s1 == nil {
		fmt.Println("没有初始化") // 执行
	}

	s2 := make([]int, 0, 4)
	fmt.Println(s2)
	if s2 == nil {
		fmt.Println("没有初始化") // 执行
	}

	// 2 map的定义并初始化
	var m21 map[string]int = map[string]int{"name": 12, "age": 19}
	var m22 = map[string]int{"name": 12, "age": 19} // 简写
	m23 := map[string]int{"name": 12, "age": 19}
	m24 := map[string]int{"name": 12, "age": 19}
	fmt.Println(m21, m22, m23, m24) // map[age:19 name:12] map[age:19 name:12] map[age:19 name:12] map[age:19 name:12]

	// 3 map的使用,取值,赋值
	m3 := map[string]int{"name": 12, "age": 19}
	fmt.Println(m3["age"]) // 取值
	m3["age"] = 99         // 赋值存在的则修改值
	m3["sex"] = 1          // 赋值不存在的则添加值
	fmt.Println(m3)        // map[age:99 name:12 sex:1]

	// 取不存在的值,不报错,取出value值的0值
	fmt.Println(m3["hobby"]) // 0

	//根据key取value,判断value是否存在
	_, ok := m3["hobby"] // 可以使用两个值来接收,第二个值是个布尔值,如果ok为true,说明存在,否则不存在  range
	fmt.Println(ok)      //false
	m3["hobby"] = 66
	_, ok1 := m3["hobby"]
	fmt.Println(ok1) // true

	// 3.2 删除元素  内置函数  只能根据key值删除
	m4 := map[string]int{"name": 12, "age": 19}
	delete(m4, "name")
	delete(m4, "hobby") // 删除不存在的,不会报错
	fmt.Println(m4)

	// 4 map长度  总共有多少个元素
	m5 := map[string]int{"name": 12, "age": 19}
	m5["xx"] = 12
	m5["xx1"] = 12
	m5["xx2"] = 12
	m5["xx3"] = 12
	fmt.Println(len(m5)) // 6
	// fmt.Println(cap(m5))  // 报错,没有容量这一说,map类型可以无限扩容

	// 5 map是引用类型,零值是 nil,引用类型一定要初始化,值类型不需要初始化就有零值
	// 当参数传递
	m6 := map[string]int{"name": 12, "age": 19}
	fmt.Println(m6)
	test5(m6)  // 函数在最下面定义
	fmt.Println(m6) // 会被改掉,应用类型

	// map 之间不能用 == 判断,== 只能用来检查 map 是否为 nil
	/*
		m7 := map[string]int{"name":12,"age":19}
		m8 := map[string]int{"name":12,"age":19}
		fmt.Println(m7==m8)  报错:map can only be compared to nil

		s7:=[]int{4,5,6}
		s8:=[]int{4,5,6}
		fmt.Println(s7==s8)  报错:map can only be compared to nil
	*/

	// 引用类型不能比较 ,值类型能比较
	// 数组的长度也是类型的一部分,长度不一样,不是同一个类型
	a9 := [3]int{4, 5, 6}
	a10:=[3]int{4,6,6}
	//a10 := [4]int{4, 6, 6}  // 加了这句则报错、不同类型不能比较
	fmt.Println(a9 == a10) //  false


	// map 可以无限制的添加值,只要内存够用,自动扩容   因为map是引用类型,扩容直接添加新map引用到上一个map即可
	// 数组长度固定,一旦定义不能改变---》为什么?  因为数组是值类型,要定义好放值的大小,后面添加值才好有位置放
}

func test5(m map[string]int) {
	m["age"] = 99
	fmt.Println(m)
}

posted @ 2022-02-15 16:45  简爱cx  阅读(75)  评论(0编辑  收藏  举报