【Go】函数高级 包的使用 gin框架入门 if-else 循环 switch 数组

昨日回顾

# 1 go 基础数据类型
	-数字:整数,正整数,浮点数,复数
    -字符串: ""  ``
    -单引号引起来的,只能放一个字符   a   中
    	-实际上是数字表示  20013
        -默认是int32  实际上是rune
        -也可定义为 byte类型或uint8类型  
    -布尔类型 :true,false
    
# 2 常量 :后期不能修改
	-作用域范围
    -同时定义多个常量
    -iota
    
# 3 函数
	-1 没有参数没有返回值 fun 函数名(){}
    -2 有参数没有返回值 fun 函数名(a int){}
    -3 多个参数同一种类型,可以简写 fun 函数名(a,b int){}
    -4 多个参数同一种类型,有返回值可以简写 fun 函数名(a,b int)int{  retrun a+b}
    -5 多个返回值fun 函数名(a,b int)(int,string){  retrun a+b,'成功'}
    
# 4 函数高级
	-1 匿名函数,定义在函数内部,没有名字
    	var f func()=func(){}
       	头等函数
        一等公民
    -2 闭包函数
    	-定义在函数内部,对外部作用域有引用
        
    -3 函数也是一种类型,参数和返回值都是类型的一部分
    
    -4 一个函数可以返回另一个函数
    
# 补充
go的值类型和引用类型。
装饰器是一种设计模式。go语言虽然没有装饰器语法糖,但还是可以实现装饰器。
设计模式还有装饰器模式、迭代器模式。

今日内容

1 函数高级

package main

import "fmt"

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

// test3 函数,接收一个参,参数是函数类型:没有参数没有返回值
// test 有返回值,返回值是个函数:函数有两个参数,一个返回值
//func test3(a func()) func(int, int) int {
//	a()
//	return func(x, y int) int {
//		return x + y
//	}
//}

// 2 类型重命名

// 可以给类型重命名
// 如果 type Myint int  相当于我们新定义了一个类型,叫Myint
// 如果 type 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
	}
}
// 我们可以定义自己的类型,简化重复的类型声明


// 3 函数可变长参数
// 可以传任意长度的int类型参数
func test4(a ...int) {
	fmt.Println(a)      // [1 2 3 4 5] 切片
	fmt.Printf("%T", a) // 类型是 int类型切片 []int
}

// 4 defer  关键字
func main() {

	//var a Myint = 9
	//var b int = 19
	//fmt.Println(a + b)
	//fmt.Println(6,3,4,5,5,6,76,7,8)
	// 完整定义
	//var f MyFunc = test3(func() {
	//	fmt.Println("被传入的函数")
	//})
	//res := f(10, 19)
	//fmt.Println(res)

	// 3 可变长参数
	//test4(1 2 3 4 5)

	// 4 defer  延迟调用, 当前函数所有代码都执行完了,再执行defer的内容,先注册,后调用 ,先写的defer后来执行
	//var a = 10
	//defer func(i int) {
	//	fmt.Println(i)
	//	fmt.Println("我很帅")
	//
	//}(a)

	defer fmt.Println("我很帅")
	defer fmt.Println("我很帅222")
	//a = 99
	fmt.Println("我不觉得")
	fmt.Println("我也不觉得")

}

go传入函数:

image-20230426101308510

可见函数类型的完整定义比较麻烦,所以我们可以进行类型重命名,简化代码,这需要使用type关键字:

image-20230426101617126

类型不匹配的情况,需要加等号,让两个类型之间可以计算:

image-20230426101803600

即如果type Myint int 相当于新定义了一个类型,新类型比如Myint是不能和int做运算的。而使用等号的方式只相当于取别名。

image-20230426101932612

println是有返回值的:

image-20230426102204905

可变长参数:

image-20230426102613616

结果a会返回一个切片类型:

image-20230426102718092

defer关键字:
defer的意思是延迟调用。

使用defer:
image-20230426103043494

对函数使用defer:

image-20230426103112661

闭包函数使用defer:

image-20230426103201674

值传递的情况:

image-20230426103326924

闭包函数是多了一种传参的方式,也就是引用传参。
defer会等待当前函数的所有代码都执行完了,再执行defer的内容。

多个defer的执行顺序:

image-20230426103528631

也就是先写的defer一定后来执行。

defer的作用:
defer可以用于文件相关操作(原理f.open 。defer f.close)。go语言没有try..except,go使用defer做异常捕获。

2 包的使用

# python 模块和包
	-模块是一个py文件
    -包是一个文件夹 有 __init__
    
# go 包 ---》包是在一个文件夹下,这个文件夹下所有go文件的第一行要声明包
	一堆go文件的组合
    
# 使用步骤
	-新建文件夹,写多个go文件,包名必须一致
     
# 注意点:
'''
// 1  包内部,大写开头,表示导出 变量,函数
// 创建utils,新建文件s1.go
import "fmt"
// 大写表示该函数导出,小写表示隐藏属性
func Add(a,b int)int{
    return a+b
}
func test(){
    res := Add(4,3)
    fmt.Println(res)
}
这个包是执行不了的,需要有main函数。
// 在main包导出
package main 
import (
	"go_day03/utils"
)
func main(){
    // 调用Add
    res:= utils.Add(4,5)
    fmt.Println(res)
}

// 2  包内部的变量函数,只能定义一次
// 新建s2.go
package utils
func Test1(){
    // 同一个包下,使用变量和函数
    Add()
}

// 3  包内部的所有东西,在包内部直接使用
在同一个包内,算作同一个作用域。

// 4 包名可以跟文件夹名不一样,但是一个文件夹下只能有一个包
// 修改utils/s1.go文件
package lqz
// 将 utils --> lqz,这样做就需要将文件夹下的所有文件第一行都改成 package lqz。


// 5 导入包,按路径导入,如果不重命名,就是文件夹必须跟包名一样
// 在别的地方导入,就不能这样使用了
import  "go_day03/utils"
func main(){
    res:= utils.Add(4,5)
    fmt.Println(res)
}
// 这样可以使用
import  "go_day03/utils"
func main(){
    res:= lqz.Add(4,5)
    fmt.Println(res)
}
// 推荐写成
import lqz "go_day03/utils"
func main(){
    res:= lqz.Add(4,5)
    fmt.Println(res)
}
// 或者将包的名字命名为文件夹的名字,就可以直接导入使用。
// 总结:
// 如果文件夹跟包名不一样,要重命名,可以命名成任意的,但是我们叫了包名    
import lqz "go_day03/utils"
// 可以命名成任意的   
import qqq "go_day03/utils"
// 以后使用包名.  调用即可

// 6 包内的init函数,可以定义多次,只要导入包,就会依次执行init
init可以写多个,不会报错。
只要导入包,所有的init函数都会执行。
同一目录下按照按照文件名排列顺序执行,比如先执行s1.go, 后执行s2.go.
同一文件内init按照从上往下的顺序,依次执行。

// 7 导入包,必须使用,不使用就报错。现在想导入但是不用,只想执行init
import _ "go_day03/utils"

// 8 一个文件夹下可以再建文件夹建新的包,各个文件夹直接没有必然联系,只是文件夹层级关系
在建立新的包。新的go文件:
/utils/lqz/s1.go

package lqz
import (
    "fmt"
    "go_day03/utils"
)
func Add(){
    fmt.Println("add")
    // 调用utils的add
    utils.Add()
}

// 9 使用的go mod模式,从1.11后都是这种模式,项目根路径下会有一个go.mod
使用 go get 命令下载第三方包时,Go Modules 会根据 go.mod 文件中记录的依赖关系,从远程仓库下载并存储在 $GOPATH/pkg/mod 目录下。如果已经存在于该目录中,则直接使用已经下载的包。
'''

# 之前有个go path模式,已经弃用了,它的包导入,不是从项目路径下开始导入,而是从go path 的src路径下路径下开始导入
go env // 里面会有一个go path
比如我们之前创建的utils包,如果这个包在go path的src目录,我们在项目中就不需要写了,可以直接导入。

3 gin框架使用


# gosdk内置包,自定义包和第三方包
	-内置包 fmt\time包
	-gin:开一个web服务
    -安装第三方包
        
# 使用gin
	配置代理:七牛云
    	-局部:goland中:GOPROXY=https://goproxy.cn,direct
        -全局:改全局go env
    安装: go get github.com/gin-gonic/gin
    
    使用fresh启动热部署:
    在项目路径下:
        go get github.com/pilu/fresh
        go install github.com/pilu/fresh@latest
        fresh
    
    写代码:
    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",
                "message": "成功",
            })
        })
        r.GET("/index", func(c *gin.Context) {
            c.HTML(200, "index.html", gin.H{"name": "lqz", "age": 19}) // 使用模板,传递变量
        })
        r.Run() // listen and serve on 0.0.0.0:8080
    }

// 在index.html文件内使用模板语法
{{.name}}  {{.age}}
// 需要注意的是要加上这个点,这跟django模板语法不同。    

go中只有模块的概念,没有包的概念。
包名建议叫文件夹的名字,但是可以不是。
函数、变量需要导出才能在外部使用,大写字母开头表示导出。java中就是共有和私有函数。
同一个包内,变量、函数不能重复定义。
同一个包内,使用变量和函数可以直接使用,无需导入。
一个包可能是有多个Go文件写的。
导入包,按照路径导入,如果不重命名,文件夹名必须和包名一致
如果文件夹和包名不一致,需要对包重命名。可以命名成任意的,但是我们叫了包名。
包内的init函数可以定义多次,只要导入包就会依次执行init.
我想导入包但是不使用,只想让包执行init函数;

image-20230426110229889

第三方包:

go语言没有包仓库,大家的代码就放在github上面。

go设置代理:
当前项目局部设置代理:

image-20230426113040937

也可以全局修改:
go env -w GOPROXY=https://goproxy.cn,direct,
gin依赖了一些,go官网的一些模块,不仅仅是去github下载项目,所以需要一些代理,配置七牛云代理,让gin去七牛云下。

4 if-else

// 基本格式
    if 条件{

    }else if 条件{

    }else{

    }
// 条件代码块,必须放bool类型
package main

import (
	"fmt"
)

// if-else

func main() {
	score := 55
	if score >= 90 {
		fmt.Println("优秀")
	} else if score >= 80 && score < 90 {
		fmt.Println("良好")
	} else if score >= 60 {
		fmt.Println("及格")
	} else {
		fmt.Println("不及格")
	}

}

5 循环

# 不同语言的循环方式
# python 
	while循环, for循环
# go 
	for循环
# java  
	while循环 , for循环 , do while循环
# go的for循环能实现while循环的功能

go的for循环实现while循环的功能:

image-20230426115203563

package main

// 循环

func main() {
	// 1 基本语法  for关键字 定义变量i=0;i<10;i++{}  三部分都可以省略,但是一般会保留第二部分,第二部分是条件
	//// 2 循环打印0--9
	//for i := 0; i < 10; i++ {
	//	fmt.Println(i)
	//}
	//fmt.Println(i)  // i的作用域范围只在for内部有效

	// 3 循环打印0--9   省略掉第一部分  分号不能省
	//i := 0
	//for ; i < 10; i++ {
	//	fmt.Println(i)
	//}
	//fmt.Println(i) // 10

	// 4 循环打印0--9   第三部分  分号不能省
	//for i := 0; i < 10; {
	//	fmt.Println(i)
	//	i++
	//}

	// 5 循环打印0--9   省略第一部分和第三部分  分号能省略
	//i := 0
	//for i < 10 {
	//	fmt.Println(i)
	//	i++
	//}

	// 6 for 条件 {}   while 循环
	//for true {
	//	fmt.Println("llll")
	//}

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

	// 7  上面是基于索引的循环,这个案例是基于迭代的
	s := "lqz国中"
	//for i, v := range s {
	//	fmt.Println(i)  // 字符串的索引
	//	fmt.Println(string(v))  // 字符串的值,输出的是该字符在Unicode编码对应的数字,比如汉字‘我’,对应的是25105
	//}
    // 请注意len获取的是字节长度,lqz占一个字节,每个汉字占三个字节。
	//for i := 0; i < len(s); i++ {
	//	fmt.Println(string(s[i]))
    // 汉字要三个字节才能正常解码,这里相当于将汉字的第一个字节拿来解码,依次解码,所以解码不出原汉字,乱码了。
	//}
    nums := [4]int{25105, 21916, 27426, 20320}
	for _, v := range nums {
		fmt.Print(string(v))
	} // 运行...
}

// 8 break和continue:

for i := 0; i < 10; i++ {
		if i == 5 {
			break
		}
		if i == 2 {
			fmt.Println("到达2")
			continue
		}
	}

range是基于迭代的循环:

image-20230426115719054

range循环字符串是按照字符进行循环、如果用len循环字符串是按照字节(有坑)。

6 switch

switch 是一个条件语句,用于将表达式的值与可能匹配的选项列表进行比较,并根据匹配情况执行相应的代码块,优雅的替换掉else-if



package main

import "fmt"

func main() {
	// 1 switch 基本使用
	//score := 90
	//switch score {  // 来判断score
	//case 90:
	//	fmt.Println("我是90")
	//case 80:
	//	fmt.Println("我是80")
	//case 70:
	//	fmt.Println("我是70")
	//}

	//// 2 default 的使用
	//score := 99
	//switch score {
	//case 90:
	//	fmt.Println("我是90")
	//case 80:
	//	fmt.Println("我是80")
	//case 70:
	//	fmt.Println("我是70")
	//default:
	//	fmt.Println("不知道")
	//}

	// 3 多表达式判断
	//score := 66
	//switch score {
	//case 90, 91, 92, 98, 99:
	//	fmt.Println("我是90")
	//case 80, 88:
	//	fmt.Println("我是80")
	//case 70:
	//	fmt.Println("我是70")
	//default:
	//	fmt.Println("不知道")
	//}

	// 4 无表达式
	//score := 66
	//switch {
	//case score > 90:
	//	fmt.Println("我是90")
	//case score > 80 && score < 90:
	//	fmt.Println("我是80")
	//case score >= 60:
	//	fmt.Println("大于60")
	//default:
	//	fmt.Println("不知道")
	//}

	//5 Fallthrough   默认情况下,每个条件之间完,默认加break,但是也不用加,其他语言要加,其他语言去掉break,会无条件执行下一个case
	// 要无条件执行下一个case,需要使用fallthrough
	score := 99
	switch {
	case score > 90:
		fmt.Println("我是90")

	case score > 80 && score < 90:
		fmt.Println("我是80")
		fallthrough // 无条件执行下一个case
	case score >= 60 && score <= 80:
		fmt.Println("大于60")
		fallthrough
	default:
		fmt.Println("不知道")
	}
}
只要走到fallthrough,可以无条件执行下一个case。

7 数组

// 定义:
数组是同一类型元素的集合。例如,整数集合 5,8,9,79,76 形成一个数组。
// Go 语言中不允许混合不同类型的元素,例如包含字符串和整数的数组。而python可以。
// 数组是连续存储,存储同一个类型的数据结构
// python 写个单链表

// 定义一个数组:
var a [3]int // 长度为3,int类型
// 使用
a[0] = 99

如下是定义了长度为3,int类型的数组:每个格子,都只能放int类型。不允许存储不同类型元素。

image-20230426154917237

格的大小不一样是不行的,上图这种是错误的。
python的列表,里面放的是地址,地址可以指向任何数据类型,数字,字符串,字典...

深浅拷贝:

image-20230426155025475

Cpython源代码里面有列表字典底层存储。

image-20230426122930440

需要给出类型,不然无法类型推导。
内存溢出,我的应用程序取出别的程序内存存储的东西。
这个数组是直接从内存地址取东西。

go数组一旦定义,大小是不能改变的,类型也不能改变。而列表是可以追加值的,python append会做扩容:

image-20230201223335526

也就是产生新的数组,然后变量指向新的数组。

链表:
数组是连续存储,链表不是连续存储:

image-20230426155348870

链表修改值的时间复杂度是On
数组删除值的时间复杂度是On

如何判断链表有没有环?
也就是链表中间打结。

posted @ 2023-04-26 15:57  passion2021  阅读(124)  评论(0编辑  收藏  举报