Go入门基础03

函数高级

给类型重命名:

	函数的参数和返回值都是类型的一部分,函数可以赋值给一个变量


 1.	例:type MyFunc func(int, int) int
  MyFunc就是我们将func(int, int) int重新定义了一下类型,以后在定义函数类型的是时候直接使用MyFunc就是代替了func(int, int) int

 2.例:type Myint =int
    Myint只是我们将int赋值给了Myint,并不是重新定义
    在之后的使用中,Myint的参数和int的参数是可以进行运算的
    
type MyFunc func(int, int) int
type Myint = int

func test3(a func()) MyFunc {
	a()
	return func(x, y int) int {
		return x + y
	}
}    
    

函数可变长参数

在go语言中函数时可以使用可变长参数来接收不定数量的参数。可变长参数由省略号 ...和类型名组成,例如:‘ ...int’表示一个接受任意数量的int类型参数的可变长采纳数。

  • 函数使用可变长参数时,可以像普通函数一样使用他们。可变长采纳数实际上就是一个切片,函数可以通过循环遍历切片来处理他们。

举个例子:

func sum(num ..int)int{
    total :=0
    for _,num :=range nums{
        total +=num
    }
    return total
}

func mani(){
    fmt.Println(sum(1,2,3))  // 输出:6
    fmt.Println(sum(4,5,6,7))  //输出 22
    fmt.Println("%T",num)  // 类型是:int类型切片 []int
}
  • 在上面的例子中,‘sum’函数使用可变长参数 ‘nums ...int’来接收任意数量的整数参数。在函数中,使用‘range’循环遍历‘nums’切片,计算他们的总和并返回,在‘main’函数中,我们使用‘sum’函数分别计算传递给他们的两个整数序列的总和。

defer关键字

  • defer 是 Go 语言中的一个关键字,用于注册一个函数调用,在函数返回时才被执行。

  • 在 Go 语言中,函数可以通过 defer 关键字注册一个延迟执行的函数调用,这个函数会在包含它的函数返回时执行。使用 defer 可以将一些必须要执行的代码(如释放资源、关闭文件等)放在函数结束前执行,以避免遗漏或者因为某些原因而忘记执行这些代码。 defer 还可以被用来在出现异常时处理一些清理工作。

  • defer 关键字后面跟着要执行的函数或方法的调用。在函数执行到 defer 语句时,被调用的函数并不会立即执行,而是会被放入一个栈中,等到函数执行完毕返回时再执行。如果函数在执行过程中出现了 panic,那么这些被延迟的函数也会在 panic 发生时按照后进先出的顺序执行。

延迟调用:;当前函数所有代码都执行完毕之后,在执行defer的内容,先注册,后调用,如果有两个defer,先写的defer后执行(类似是先进后出)

** 例子:**

func main(){
    defer fmt.Println("Hello")
    fmt.Println("World")
}

例子总结:

  • 上面的代码会先输出 "World",然后再输出 "Hello"。因为 defer 的执行顺序是后进先出,所以 "Hello" 会在 "World" 之后输出。

  • 需要注意的是,defer 中注册的函数或方法的实参是在注册时就会被求值,而不是在函数返回时再被求值。这意味着,如果函数执行过程中发生了变量的改变,那么被注册的函数调用时使用的是当前变量的值,而不是注册时的值。

包的使用

在python中的  模块和包
	 - 模块是一个py文件
	- 包是一个又__init__文件的文件夹

# 在go中 包是在一个文件夹下,这个文件夹下所有Go文件的第一行都需要声明包
	--就是一堆Go文件的组合

使用步骤:
	- 新建一个文件夹,写多个go文件,包名必须是一致的

PS:
	1. 在包的内部,想要被其他的包下使用这个包中的变量,函数,就需要将函数,变量 的'首字母大写'
	2.在包的内部的变量,函数名只能命名一次
	3.包内部的所有东西只要是在这个包的内部都是可以直接使用的
	4.包的名字可以和这个文件名不一样,但是一个文件夹下只能有一个包
	5.导入包,是按照路径进行导入的,如果不重命名的话,就是文件夹必须和包名一样
		如果文件夹跟包名不一样,就需要重命名,可以命名成任意的,但是一般是使用包名(包内部命名的包名)
		import ff  "go_03/utils"
		
		也可以是再这个包下重新命名
		import qqq  "go_03/utils"
	以后在使用包名调用即可

	6.包内部中有init函数可以被定义多次,只要导入包,就会依次执行 包中的init函数

	7.导入包,必须使用,不使用的话就会报错,现在不使用,只想执行init ,就可以 
	import _  "go_03/utils"

	8.一个文件夹下可以再创建一个文件夹,可以在这个文件夹下再创建一个包,但是这两个文件夹是没有联系的,只是简单的文件的层级关系。
	
	9.使用的Go mod模式,从1.11后都是使用这种模式,项目路径下会有一个go.mod文件,不要动它


// 早期有个go path 模式,已经被弃用了,它在导入包的时候,不是从项目路径下开始导入的,而是从go path 的src 路径下路径开始导入的

包的使用:

gin官网:https://pkg.go.dev/github.com/gin-gonic/gin#section-readme

go sdk 内置包,自定义包,第三方包

	-gin : 可以开启一个web服务
	- 安装第三方包

# 使用 gin
	gin我们想要下载就是子啊GitHub上下载,会很慢,而且可能会出现下载不了的现象。所以我们可以使用配置代理: 使用七牛云代理
	局部配置: Goland 中:
	GOPROXY=https://goproxy.cn,direct
	
	全局配置: 更改全局 go env
	安装:go get  github.com/gin-gonic/gin

代码例子:
package main

import "github.com/gin-gonic/gin"

func main() {
	r := gin.Default()
	r.LoadHTMLGlob("templates/*")
	r.GET("/", func(c *gin.Context) {
		c.JSON(200, gin.H{
			"code":    "100",
			"meassge": "发送成功",
		})
	})
	r.Run()
	

流程控制语句

if -else

# 语法格式
if 条件{
    
}else if 条件{
    
}else{
    
}
PS:{}必须挨着条件,否则就会报错

循环:

在go中只有for循环

在python中 有while for

java中 有while ,for ,do while

go中能是用for实现while循环功能

案例:

package main

import "fmt"

// 循环

func main() {
	// 1.基本语法  循环打印0-9
	/*	for i := 0; i < 10; i++ {
		fmt.Println(i)
	}*/
	// 2. i 的作用域范围只会在for 内部有效
	//i := 0
	//for i := 0; i < 10; i++ {
	//	fmt.Print(i) // 0123456789
	//}
	//fmt.Println(i) //0
	// 3.省略第一部分 分号不能省
	//i := 0
	//for ; i < 10; i++ {
	//	fmt.Println(i) // 0,1,2,3,4,5,6,7,8,9
	//}
	//fmt.Println(i) // 10

	// 4.省略第三部分 分号也不能省
	//for i := 0; i < 10; {
	//	fmt.Println(i)
	//	i++
	//}

	// 5. 省略第一部分和第三部分,分号也不用写了
	//i := 0
	//for i < 10 {
	//	fmt.Println(i)
	//	i++
	//}

	// 6.for 条件{}  写成while循环  死循环
	//for true {
	//	fmt.Println("aaaa")
	//}

	// 7. 死循环
	//for {
	//	fmt.Println("bbbbbb")
	//}

	// 8.上面都是基于索引的循环,下面这个案例是基于迭代的
	s := "lover中国"
	//for i, v := range s {
	//	fmt.Println(i)
	//	fmt.Println(string(v))
	//}

	for i := 0; i < len(s); i++ {
		fmt.Println(string(s[i])) // 打印的就是乱码
	}
}

switch

  • 在go语言中,switch语句用于根据不同的条件执行不同的代码块。它可以被认为是一个更灵活和可读性更高的if-else语句。

switch的语句基本语法:

switch expression {
    case value1:
        // 执行代码块1
    case value2:
        // 执行代码块2
    ...
    default:
        // 执行默认代码块
}

详解:其中,expression是要被检查的表达式,value1value2等是与表达式进行比较的值。如果expression的值等于其中一个case后面跟随的值,那么与该case匹配的代码块将被执行。如果没有任何一个case匹配,则执行default代码块(如果有)。

简单案例:

import "fmt"

func main() {
	num := 2
	switch num {
	case 1:
		fmt.Println("这是数字1")
	case 2:
		fmt.Println("这是数字2")
	default:
		fmt.Println("不知道是什么数字")

	}
}

详解:在上面的示例中,如果num等于1,那么输出"这是数字1";如果num等于2,那么输出"这是数字2";如果num不是1、2,那么输出"不知道是什么数字"。

Switch的多中使用方式:

package main

import "fmt"

func main() {
	//num := 2
	//switch num {
	//case 1:
	//	fmt.Println("这是数字1")
	//case 2:
	//	fmt.Println("这是数字2")
	//default:
	//	fmt.Println("不知道是什么数字")
	//
	//}

	// 多表达式判断
	//num := 38
	//switch num {
	//case 3, 4, 5:
	//	fmt.Println("这是数字1")
	//case 2, 8, 9, 20:
	//	fmt.Println("这是数字2")
	//default:
	//	fmt.Println("不知道是什么数字")
	//
	//}

	// 无表达式
	//num := 48
	//switch {
	//case num > 40:
	//	fmt.Println("数字大于40")
	//case num > 60 && num < 40:
	//	fmt.Println("数字大于60")
	//default:
	//	fmt.Println("不知道是什么数字")
	//}

	// Fallthrough 在默认的情况下,每个条件之间执行完毕默认会加上break
	// 但是也可以不用加,其他的语言是要加的,其他得语言去掉break就会执行
	// 下一个 case
	//在go 中要无条件执行下一个case ,及需要使用到 fallthrough

	num := 38
	switch {
	case num < 20:
		fmt.Println("数字小于20")
	case num < 40 || num > 30:
		fmt.Println("这是数字小于40大于30")
		fallthrough
	default:
		fmt.Println("不知道是什么数字")
		
	就会打印		
	// 这是数字小于40大于30
		//不知道是什么数字
		
	}
}

数组

  • 在go语言中,数组是一种用于存储固定数量的数据结构。数组中的每一个元素都有一个唯一的索引,可以使用这个索引来访问特定的元素。

创建数组:

  • 数组是值类型的
	-数字,字符串,布尔,数组,都是值类型,是真正直接存数据的
	-切片,map,指针 是引用类型的,他们都是指向一个地址,指向了一个人具体的值
  • 想要创建一个数组,需要指定数组的长度和元素的类型如下:
var arr[3] int 
这就是创建一个包含3个整数的数组
  • 也可以使用字面量初始化数组
arr :=[3]int{1,2,3}

访问数组元素

  • 想要访问数组元素,可以使用数组的索引。例如。想要访问arr数组的第一个元素,可以使用:
	数组是值类型,go语言中函数传参是copy传递,复制一份采纳数,传入,当参数传递,在函数中修改,并不会影响原来的
fmt.Println(arr[0])  // 就会输出1

遍历数组

  • 想要遍历数组,可以使用for循环和数组的长度。例如:下面就是遍历arr数组中所有的元素,
for i := 0; i<len(arr); i++{
    fmt.Println(arr[i])
}

// 也可以使用range关键字来遍历数组,它会返回数组的索引和值:
for index,value := range arr{
    fmt.Println(index,value)
}

数组的长度

  • 想要获得数组的长度,可以使用len函数。例如:
fmt.Println(len(srr))  // 输出3

数组切片

  • 数组可以使用切片来访问一部分的元素,切片是一个指向数组的指针,它包含一个开始索引和一个结束索引。
  • 如下代码创建了一个包含arr数组的前两个元素的切片:
slice :=arr[0:2]

// 也可以使用省略来简化切片操作,如下

slice := arr[:2]

数组的修改

  • 可以通过索引来修改数组的元素,如下将数组的第一个元素修改为10,
arr[0]=10  // 输出 [10,0,0]

多维数组

  • 多维数组是指数组中包含其他的数组的数组。他们通常用于表示二维表格和矩阵等复杂数据结构,在go语言中,可以创建任意维度的数组,只需要指定每一维度的长度即可。

  • go语言可以创建多维数组,如下:

创建一个包含3行4列的二维数组:
	var matrix[3][4]int
// 这个数组包含3个子数组,每个子数组包含4个整数。要访问数组中的元素,需要使用两个索引,一个表示行数,另一个表示示例数,如下:
value :=matrix[1][2]


//多维数组的初始化可以使用嵌套的花括号:如下:

matrix :=[3][4]int{
    {1,2,3,4},
    {5,6,7,8},
    {9,10,11,12},
}
// 这个数组的每一个子数组包含4个整数,分别是初始化为
1、2、3、4,5、6、7、8和9、10、11、12。

//在go语言中,多维数组的每个维度都可以单独遍历,可以使用嵌套的for循环来遍历多维数组的每一个元素,如下遍历所有matrix数组的所有元素

for i := 0; i<len(matrix);i++{
    for j:=0; j<len(matrix[i]);j++{
        fmt.Println(matrix[i][j])
    }
}

代码:

package main

import "fmt"

func main() {

// 2.数组长度
// 定义数组
//var a [3]int = [3]int{3, 4, 5}
//a:= [3]int{3,4,5}
// 初始化几位数,它的长度就是多少
//a := [...]int{1, 2, 3, 4, 5, 6}
//fmt.Println(len(a))   // 6
//fmt.Printf("%T\n", a) //[6]int

// 3. 循环打印数组
//a := [...]int{1, 2, 3, 4, 5, 6}
// 基于索引打印
//for i := 0; i < len(a); i++ {
//	fmt.Println(a[i])
//}
// 基于迭代的  range
//for i, v := range a {
//	fmt.Println(i)  // 打印的是索引
//	fmt.Println(v)  // 输出的是值  不想要索引,就用_,来代替
//}

// 4.多维数组
//var a [3][4]int  // 这是没有初始化的数组
//var matrix [3][4]int
//var a [3][4]int = [3][4]int{
//	{1, 2, 3, 4},
//	{5, 6, 7, 8},
//	{9, 10, 11, 12},
//}
//fmt.Println(a)

// 循环多维数组
//for i := 0; i < len(a); i++ {
//	for j := 0; j < len(a[i]); j++ {
//		fmt.Println(a[i][j])
//
//	}
//}
// range循环
//for _, vle := range a {
//	for _, ve := range vle {
//		fmt.Println(ve)
//	}
//}
// 5.数组定义并赋初始值,将数组中第99赋值为1,其他都是0
var a [100]int8 = [100]int8{98: 1}
fmt.Println(a)

}

func test(a [3]int) {
a[0] = 9
fmt.Println(a) // [9 4 5]

}


ps:多维数组在一些算法和数据处理场景中非常有用,可以有效的表示和操作数据结构。

切片 (slice)

  • 切片是由数组建立的一种方便,灵活且功能强大的包装。切片本身不拥有任何数据。他们只是对现在有数据的【引用】
-本身不存储数据,是对底层数组的引用
    	-切片的数据结构
        	{
                len:2
                cap:4
                point:0x3456
            }
package main

import (
	"fmt"
)

func main() {
	// 1.切片的定义   [] 里面不放入任何东西,就是切片类型,如果里面放了数字,就是数组类型
	//var a []int // 这只是定义了一个切片,并没有初始化,是引用类型, 没有初始化打印的结果是nil类型
	//fmt.Println(a[0])  // 没有初始化,使用就会报错

	// 定义并初始化
	//var b [10]int      // 这是值类型 ,会有默认值
	//var a []int = b[:] // 表示a这个切片,对数组进行了引用,引用了从0到最后
	//fmt.Println(a)
	//fmt.Println(a[0]) // 输出0

	// 2. 切片取值
	//fmt.Println(a[8]) //0

	//切片的修改会影响到底层的数组,数组修改也会影响到切片
	//b[0] = 11
	//a[0] = 11
	//fmt.Println(b)
	//fmt.Println(a)

	// 3.基于数组,获得切片
	//var b [10]int = [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}
	//var a []int = b[0:3] // 表示a这个切片,对数组进行引用,引用从0-3

	//fmt.Println(a)  // [1,2,3]
	//a[0] = 99
	//fmt.Println(a) //[99 2 3]
	//fmt.Println(b) //[99 2 3 4 5 6 7 8 9 0]
	//var a []int = b[6:9]
	//fmt.Println(a)  //[7 8 9]
	//a[0] = 99
	//fmt.Println(a) // [99 8 9]
	//fmt.Println(b) // [1 2 3 4 5 6 99 8 9 0]
	//b[0] = 88
	//fmt.Println(b)  //[88 2 3 4 5 6 99 8 9 0]
	// 尾部追加
	//a = append(a, 66)
	//fmt.Println(a) // [7 8 9 66]
	//fmt.Println(b) // [1 2 3 4 5 6 7 8 9 66]

	// 切片的长度和容量
	//fmt.Println(len(a)) // 3
	//fmt.Println(cap(a)) // 4 容量就是能存的数是4,这个不是取决于底层数组的大小,而是取决于切片切数组的位置

	// 切片如果追加值超过了底层数组的长度,就会自动扩容。(翻倍扩容)容量在1024内
	//a = append(a, 11, 22, 33, 44, 55, 77)
	//
	//fmt.Println(len(a)) // 9
	//fmt.Println(a)      // [7 8 9 11 22 33 44 55 77]
	//fmt.Println(cap(a)) // 10
	//
	//// 这时候该底层数组的值  数组和切片就没有联系了,不会影响到切片的值
	//b[9] = 999
	//fmt.Println(b) // [1 2 3 4 5 6 7 8 9 999]
	//fmt.Println(a)  // [7 8 9 11 22 33 44 55 77]
	//
	// 8.切片定义并初始化,使用make初始化
	//var a[]int   // 是nil 没有初始化
	//var a []int = make([]int, 3, 4) //初始化,长度是3,容量是4
	//fmt.Println(a)                  //[0 0 0]
	//fmt.Println(len(a))  // 3
	//fmt.Println(cap(a))  // 4

	//var a []int = make([]int, 0, 4) // 长度是0,容量是4
	//fmt.Println(a)                  //[]
	//fmt.Println(len(a))		// 0
	//fmt.Println(cap(a)) // 4

	//a = append(a, 2)
	//fmt.Println(a)      // 2
	//fmt.Println(len(a)) //1
	//fmt.Println(cap(a)) // 4

	//9.切片的参数传递,是引用类型,函数中修改值,会影响原来的
	//var a []int = make([]int, 3, 5)
	//a[1] = 99
	//test1(a)
	//fmt.Println(a)  // [88 99 0]

	// 10 多维切片 切片定义并初始化
	//var a [][]int = [][]int{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}
	//fmt.Println(a)  // [[1 2 3] [4 5 6] [7 8 9]]

	var a [][]string = make([][]string, 3, 3)
	fmt.Println("-----", a[2])  // 会打印一个空字符
	a[2] = make([]string, 3, 3)   // []
	fmt.Println(a[2][0])
}

func test1(a []int) {
	a[0] = 99
	fmt.Println(a, 33) //[99 99 0] 33
	a[0] = 88
	fmt.Println(a) // [88 99 0]

}

可变长参数

package main

import "fmt"

func main() {
	var a []string = []string{"andy", "xxx", "hhh"}
	test3(a...) // 相当于是打散传入进去
}

func test3(a ...string) {
	fmt.Println(a)      //[andy xxx]
	fmt.Println(cap(a)) // 3
	fmt.Printf("%T", a) // []string

}

maps

package main

import "fmt"

func main() {
	// 1. 存储是以K:V的形式 定义map
	//var usreInfo map[string]string // 没有初始化
	//fmt.Println(usreInfo)          // map[]
	//
	//if usreInfo == nil {
	//	fmt.Println("这是一个nil")  // 会打印出来
	//}

	// 2 map 初始化: 一
	//var userInfo map[int]string = map[int]string{1: "lqz", 3: "pyy"}
	//fmt.Println(userInfo)  // map[1:lqz 3:pyy]
	//if userInfo == nil {
	//	fmt.Println("这是一个nil")  // 不会打印
	//}

	// make 初始化
	//var userInfo map[int]string = make(map[int]string)
	//fmt.Println(userInfo) //map[]

	// 3.初始化后才能取值赋值
	//var userInfo map[int]string=make(map[int]string)
	//var usreInfo map[int]string
	//fmt.Println(usreInfo[1])
	//usreInfo[1] = "PPP"
	//fmt.Println(usreInfo)

	// 以后所有的引用类型,都需要初始化才能用,值类型不需要,有默认值
	// 4. 取值赋值
	var userInfo map[string]string = make(map[string]string)
	userInfo["age"] = "10"
	userInfo["name"] = "小明"
	//fmt.Println(userInfo) // map[age:10 name:小明]

	//fmt.Println(userInfo["age"]) // 10

	// 如果是取不存在的值,显示的就是value 值的默认值  按Key取值,能返回一个布尔值,根据布尔值来进行判断
	//v, ok := userInfo["name"]
	//fmt.Println(v)   // 小明
	//fmt.Println(ok)   // true

	//if v, ok := userInfo["name"]; ok {
	//	fmt.Println(v)  // 小明
	//}

	// 删除map元素
	//delete(userInfo, "name")
	//fmt.Println(userInfo) //map[age:10]

	//  map 的长度
	//fmt.Println(len(userInfo)) //2

	// 引用类型
	fmt.Println(len(userInfo)) // 2
	test4(userInfo)
	fmt.Println(userInfo) // map[age:10 name:pyy]
}

func test4(u map[string]string) {
	u["name"] = "pyy"
	fmt.Println(u) // map[age:10 name:pyy]

}

posted @ 2023-04-26 19:44  亓官扶苏  阅读(12)  评论(0编辑  收藏  举报