Go语言
根据老师的视频进行课后的一个基础语法学习,从零开始,简单做了一些笔记,这里是一些针对算法可以运用的语法,后面的几点语法会在下一篇文章,当然这个笔记现在还比较捡漏,在后续的运用中应该还会有一些补充~
Go安装
go下载链接:studygolang.com/dl
再自己下个VS code,里面下一个go的扩展就好了,弄好之后可以试一下终端输入 go version,有版本号就代表安装成功了。
然后你可以配置一下环境
新建文件夹 goprogects 用于存放工程代码(可创建在任意磁盘),并在该文件夹下创建 bin、pkg、src 这三个文件夹,其中 bin 文件夹用于存放在编译项目时,生成的可执行文件,pkg文件夹用于存放在编译项目时,生成的包文件,src 文件夹用于存放编写的代码。
在系统变量中新建(GO111MODULE, on)、(GOBIN, 上一步新建的bin地址,E:\goprojects\bin)、(GOPATH, E:\goprojects)、(GOPROXY, goproxy.cn)、(GOROOT, 你安装go时候的那个目录)这5个变量,到此环境变量配置完成。其中 GOPATH 和 GOBIN 都是新建的用于存放代码的 goprogects 目录, GOROOT 是 GO 编译器安装的目录。 1、在D:\goprojects\src目录下新建文件夹命名为1,用于存放第一个go程序。
打开vscode,打开新建的 1文件夹中,新建一个 go 文件,命名为 hello.go ,之后在这个 go 文件中输入以下代码,然后按 Ctrl + s 保存代码,并运行。
package main //入口文件
import( //导入fmt包
"fmt"
)
func main(){
fmt.Println("hello world")
}
输出:
Go语言基础语法
1.1什么是Go语言
1.高性能、高并发
2.语法简单、学习曲线平缓
3.丰富的标准库
4.完善的工具链
5.静态链接
6.快速编译
7.跨平台
8.垃圾回收
package main
import(
"net/http"
)
func main(){
http.Handle("/",http.FileServer(http.Dir("."))) //在http里面新建一个路由
http.ListenAndServe(":8080",nil) //增添8080端口,启动服务器
}
1.2基础语法
1.变量
初始化:
var 变量名字 类型 = 表达式
零值初始化机制:。如果初始化表达式被省略,那么将用零值初始化该变
量。
名字:= 表达式
调用用内建的new函数。表达式new(T)将创建一个T类型的匿名变量,初始化为T类型的零值,然后返回变量地址,返回的指针类型为 *T
p := new(int) // p, *int 类型, 指向匿名的 int 变量
fmt.Println(*p) // "0"
*p = 2 // 设置 int 匿名变量的值为 2
fmt.Println(*p) // "2"
// 产生1~100的随机数:
package main
import "fmt"
import "math/rand"
import "time"
func main() {
// 设置随机数种子,time.Now().Unix():返回的1970:0:0到现在的秒数
rand.Seed(time.Now().Unix())
//生成1~100的随机数
n := rand.Intn(100) + 1
fmt.Println(n)
}
多个变量的一次性定义
// golang一次定义多个变量的三种方式:
var i1, j1, k1 int
i1, j1, k1 = 10, 10, 10
var i2, j2, k2 = 20, 20.0, "str2"
i3, j3, k3 := 20, 30.0, "str3"
fmt.Println("i1 =", i1, "j1 =", j1, "k1 =", k1)
fmt.Println("i2 =", i2, "j2 =", j2, "k2 =", k2)
fmt.Println("i3 =", i3, "j3 =", j3, "k3 =", k3)
变量的作用域
- 局部变量:函数内部声明和定义、使用;
- 全局变量:函数外声明和定义、使用,该变量在整个包中有效(如果首字母大写,则在整个程序中的其他包中也是有效);
- 当全局变量和局部变量同名时,会采用“就近原则”;
// 全局变量的定义时的注意事项:
var n1 int = 10 // 正确
n2 := 10 // 报错,因为该语句等价于 var n2 int n2 = 10,故报错发生了(第二条语句,不能在函数外进行赋值操作)
基本数据类型: 数值型(整型(int、int8(代表8位的整型)、int16、int 32、int64、uint、uint8、uint16、uint32、uint64、byte)、浮点型(float32、float64))、字符型(没有专门的字符型,使用byte来保存单个字符)、布尔型、字符串(go将string归为基本数据类型)
派生/复杂数据类型: 指针Pointer、数组、结构体struct、管道Channel、函数(也是一种数据类型)、切片slice、接口interface、map
2.3.3 基本数据类型的默认值(又称为零值)
| 数据类型 | 默认值 |
|---|---|
| 整型 | 0 |
| 浮点型 | 0 |
| 字符串" | "" |
| 布尔类型 | false |
注意:
- 格式化输出
fmt.printf()中,%v表示按照变量的值输出,%T表示输出变量的类型; - golang中查看变量占用的字节数,可以调用unsafe包中的Sizeof()函数;
- 写程序时整型变量采用 “保小不保大” 的原则;
- Golang中统一采用utf-8编码;
基本数据类型间的相互转换
- 表达式T(v),将值v转换为T类型。转换时,注意范围,防止由于溢出造成数据丢失。
- 被转换的是变量存储的数据,变量本身的数据类型并没有变化。
基本数据类型 与 string类型的转换:
package main
import "fmt"
func main() {
var (
num1 int = 90
num2 float64 = 23.33
b bool = true
m_char byte = 'a'
str string
)
fmt.Printf("num1 = %d num2 = %f b = %t m_char = %c\n", num1, num2, b, m_char)
// 方式一:使用fmt.Sprintf(),将基本数据类型转为字符串string类型
str = fmt.Sprintf("%d", num1)
fmt.Printf("str type %T str=%s\n", str, str)
str = fmt.Sprintf("%f", num2)
fmt.Printf("str type %T str=%q\n", str, str)
str = fmt.Sprintf("%t", b)
fmt.Printf("str type %T str=%q\n", str, str)
str = fmt.Sprintf("%c", m_char)
fmt.Printf("str type %T str=%q\n", str, str)
// 方式二:使用strconv包中的函数
str = strconv.FormatInt(int64(num1), 10) // 返回num1的10进制字符串
fmt.Printf("str type %T str=%q\n", str, str)
// ‘f’表示生成的字符串中小数的表示格式***.***;5表示小数点后保留5位小数;64表示num2是float64类型
str = strconv.FormatFloat(num2, 'f', 5, 64)
fmt.Printf("str type %T str=%q\n", str, str)
str = strconv.FormatBool(b)
fmt.Printf("str type %T str=%q\n", str, str)
// 整型int转字符型string
var num3 int = 3
str = strconv.Itoa(num3)
fmt.Printf("str type %T str=%q\n", str, str)
}
字符串 转换成 基本数据类型:
package main
import "fmt"
import "strconv"
func main() {
var (
num1 string = "90"
num1_ int64
num2 string = "23.33"
num2_ float64
b string = "true"
b_ bool
)
b_, _ = strconv.ParseBool(b)
fmt.Printf("b_ type %T, b_=%t\n", b_, b_)
// 10表示转成10进制,64表示转成int64
num1_, _ = strconv.ParseInt(num1, 10, 64)
fmt.Printf("num1_ type %T, num1_=%d\n", num1_, num1_)
// 64表示转成float64
num2_, _ = strconv.ParseFloat(num2, 64)
fmt.Printf("num2_ type %T, num2_=%f\n", num2_, num2_)
}
2.if else
if语句检查指定的条件,并在条件满足时执行指定的操作。
Go里的条件语句模型是这样的
if 条件 1 {
分支 1
}else if 条件 2 {
分支 2
}else if 条件 ... {
分支 ...
}else {
分支 else
}
Go是强类型,要求条件表达式必须严格返回布尔型的数据(nil和0和1都不行)
3.循环
经典for循环结构中 , for关键字后面有三个表达式,且每个表达式都可以省略
for i := 0; i < 3; i++ {
fmt.Println(i)
}
//等价于
j := 0
for ; j < 3; {
fmt.Println(j)
j++
}
for关键字后面也可以只有一个表达式,表示如果条件成立执行循环体代码
for i := 0; i < 3; i++ {
fmt.Println(i)
}
//等价于
j := 0
for j < 3 {
fmt.Println(j)
j++
}
4.switch
switch 语句用于基于不同条件执行不同动作,每一个 case 分支都是唯一的,从上直下逐一测试,直到匹配为止。 Golang switch 分支表达式可以是任意类型,不限于常量。可省略 break,默认自动终止。
switch var1 {
case val1:
...
case val2:
...
default:
...
}
变量 var1 可以是任何类型,而 val1 和 val2 则可以是同类型的任意值。类型不被局限于常量或整数,但必须是相同的类型;或者最终结果为相同类型的表达式。 您可以同时测试多个可能符合条件的值,使用逗号分割它们,例如:case val1, val2, val3。
5.数组
一维数组
1)数组可以用来存放多个同一类型的数据;
2)Go中,数组也是一种数据类型,且是值类型;
3)数据定义和内存分配:数组名、数组的第一个元素的地址,都等于数组的首地址;
package main
import "fmt"
import "strconv"
func main() {
var nums [6]float64 // 数组中元素是float64类型,故每个元素占8bytes
nums[0] = 12.0
nums[1] = 11.0
nums[2] = 13.0
fmt.Printf("%p %p %p\n", &nums, &nums[0], &nums[1])
// 分析数组在内存中的空间分配情况:可以通过数组的首地址访问到所有变量
total_nums_sum := 0.0
for i := 0; i < len(nums); i++ {
fmt.Printf("%v -> %f\t", &nums[i], nums[i])
total_nums_sum += nums[i]
}
fmt.Printf("\n")
avg_nums_sum_str := fmt.Sprintf("%.2f", total_nums_sum / float64(len(nums)))
avg_nums_sum, _ := strconv.ParseFloat(avg_nums_sum_str, 64) // 64表示转成float64
fmt.Printf("total_nums_sum=%f\n avg_nums_sum_str=%q -> avg_nums_sum=%f", total_nums_sum, avg_nums_sum_str, avg_nums_sum)
}
4)数组的四种初始化方式;
var numsArr1 [3]int = [3]int{1, 2, 3}
var numsArr2 = [3]int{1, 2, 3}
var numsArr3 = [...]int{1, 2, 3}
var numsArr4 = [3]string{1:"jary", 0:"tom", 2:"mark"}
// var numsArr4 = [3]int{1:2,0:1,2:3}
5)数组的两种遍历方式:①下标;②for - range;
package main
import "fmt"
// 数组的两种遍历方式:1)下标;2)for - range;
func printIntArr(arr *[3]int, size int) {
for i := 0; i < size; i++ {
fmt.Printf("%v ", arr[i])
}
fmt.Printf("\n")
}
func printStrArr(arr *[3]string, size int) {
for index, val := range arr {
fmt.Printf("arr[%d]=%v ", index, val)
}
fmt.Printf("\n")
}
func main() {
// 四种初始化数组的方式
var arr1 [3]int = [3]int{1, 2, 3}
var arr4 = [3]string{1:"Tom", 0:"Steven", 2:"Jarry"}
printIntArr(&arr1, 3)
printStrArr(&arr4, 3)
}
数组的使用注意事项:
1)数组是多个相同类型数据的组合,一旦声明/定义,其长度是固定的,不能动态变化;
2)var arr []int,这时arr就是一个slice切片;
3)数组中的元素可以是任何数据类型,包括 值类型 和 引用类型,但不能混用;
4)数组创建后,如果没有赋值,会有默认值(零值);
5)使用步骤:①声明数组并开辟空间;②给数组各个元素赋值(默认零值);③使用数组;
6)数组属于值类型,默认情况下是值传递,因此会进行值拷贝,数组间不会相互影响;如果想用其他函数修改原数组,可以使用引用传递(指针方式);
package main
import "fmt"
func PrintArr(arr *[3]int, size int) {
for i := 0; i < size; i++ {
fmt.Printf("%d ", arr[i])
}
fmt.Printf("\n")
}
// 使用值传递
func modify1(arr [3]int) {
arr[0] = 4
}
// 使用引用传递
func modify2(array *[3]int) {
(*array)[0] = 4
(*array)[1] = 5
}
func main() {
var arr [3]int = [3]int{1, 2, 3}
PrintArr(&arr, 3)
modify1(arr)
PrintArr(&arr, 3)
modify2(&arr)
PrintArr(&arr, 3)
}
7)长度是数组类型的一部分,在传递函数参数时,需要考虑数组的长度;
二维数组
基本语法:var arr_2D [行][列]int
package main
import "fmt"
func main() {
// 方式一:先定义/声明,再赋值
var arr_2D [3][3]int
for i := 0; i < 3; i++ {
for j := 0; j < 3; j++ {
arr_2D[i][j] = i * j
}
}
fmt.Println(arr_2D)
// 方式二:直接初始化
var arr_2D2 [3][3]int = [3][3]int{{1, 2, 3}, {1, 2, 3}, {1, 2, 3}}
fmt.Println(arr_2D2)
}
二维数组的遍历
for i := 0; i < len(arr_2D2); i++ {
for j := 0; j < len(arr_2D2[i]); j++ {
fmt.Printf("%d ", arr_2D2[i][j])
}
fmt.Printf("\n")
}
fmt.Printf("\n")
for _, address_1D := range arr_2D2 { // address_1D是二维数组中保存的每一行的地址
for _, val := range address_1D {
fmt.Printf("%d ", val)
}
fmt.Printf("\n")
}
6.切片
1)切片的基本定义:var 变量名 []类型;
2)切片的使用和数组类似,遍历切片(for - len 或者 for index, val : range slice)、访问、求切片的长度len(slice),均与数组相同;
3)切片的内存分布:内存空间中,保存了:首地址、长度、容量
4)切片slice的三种使用方式:
- 定义一个切片并让切片取引用一个已经创建好的数组,如slice := arr[start:len(arr)];
- 用make来创建切片,可指定大小和容量,且默认是零值;
- 定义一个切片,直接就指定具体数组,使用原理类似make的方式;
package main
import "fmt"
// 切片(可以理解为动态数组)的基本使用
// 内存中的布局:类似于结构体,有首地址、长度、容量
// 切片定义后,本身是空的;需要让其引用到一个数组 或者 make一个空间供切片使用
func main() {
// 切片的使用:方式一:定义一个切片并让切片取引用一个已经创建好的数组
var arr [6]int = [6]int{0, 1, 2, 3, 4, 5}
slice1 := arr[1:4] // slice[start:end] --> [start, end)
// slice1 := arr[:] // 表示切片包含了说组arr中的所有元素
fmt.Println("arr=", arr)
fmt.Println("slice=", slice1)
fmt.Println("len(slice)=", len(slice1))
fmt.Println("capability of slice is ", cap(slice1))
fmt.Printf("\n")
// 方式二:用make来创建切片
// 基本语法:make(type, len, [cap]) // cap是可选的,cap >= len
var slice2 []float64 = make([]float64, 4, 10)
fmt.Println("slice2=", slice2)
fmt.Println("len(slice2)=", len(slice2), "capability of slice2 is ", cap(slice2))
fmt.Printf("\n")
// 方式三:定义一个切片,直接就指定具体数组,使用原理类似make的方式
var slice3 []int = []int{1, 2, 3}
fmt.Println("slice3=", slice3)
fmt.Println("len(slice3)=", len(slice3), "capability of slice3 is ", cap(slice3))
}
5)切片是一个数组的引用,因为切片是引用类型,故在传递数组时,需要遵守引用传递的机制;
package main
import "fmt"
func test1() {
var arr [3]int = [3]int{1, 1, 3}
slice1 := arr[:]
var slice2 []int = slice1
slice2[1] = 2 // 此时,slice1、slice2均指向数组arr所在的内存空间
fmt.Println(arr, "\t", slice1, "\t", slice2)
// 结果是arr、slice1、slice2: [1, 2, 3]
}
// 该处调用函数时,切片形参只是拷贝了切片实参的{指向的数组的首地址, len, cap},
//故本质上只是形参和实参变量本身的地址不同,其指向的数组内存空间相同
func test_slice(slice []int) {
slice[1] = 2 // 这里修改slice指向的内存空间的数据,会改变调用该函数的切片实参
// 切片作为形参时,切片只能在函数内部修改值,不能直接添加值
slice = append(slice, 29) // 无效
fmt.Printf("%p\n", &slice)
// 表明:slice和外部调用该函数的切片实参,指向的是同一块数组内存空间
}
// 切片指针作为形参时,切片即能在函数内部修改值,又能直接添加值
func test_slice_ptr(slice *[]int){
(*slice)[1] = 1
(*slice) = append((*slice), 4)
}
func test2() {
var arr [3]int = [3]int{1, 1, 3}
slice := arr[:]
fmt.Printf("%p\n", slice)
test_slice(slice)
fmt.Println(arr, "\t", slice)
test_slice_ptr(&slice)
fmt.Println(arr, "\t", slice)
// 结果是arr、slice: [1, 2, 3]
}
func main() {
test1()
test2()
}
6)切片的长度是变化的,故可以认为切片时一个动态数组,即切片可以动态增长;
7)切片的切片:slice2 = slice1[start:end],此时slice1和slice2指向的是同一个数组空间;
8)用append内置函数,可以使切片进行动态增加;
slice3 = append(slice3, 元素1, 元素2); slice3 = append(slice3, slice2...);(...是对slice2进行解构为一个个元素)
var arr []int = []int{1, 2, 3}
slice1 := arr[:]
slice2 := make([]int, 10) // 默认值全是0
// slice1、slice2的数据空间是独立的,不会相互影响
copy(slice2, slice1) // slice2:[1,2,3,0,0,0,0,0,0,0]
// copy(para1, para2)中,para1、para2必须都是切片类型
7.map
map是key-value的数据结构,又称字段或者关联数组。
1)基本语法:map[keytype]valuetype
keytype只能是int、string、数字、bool、指针、channel、接口、结构体、数组等 (因为slice、map、function无法用==判断,故keytype不能是slice、map、function),但通常用int、string;
valuetype通常是数字(整型、浮点型)、string、map、struct等; 2)map声明举例:
var a map[string]string
var a map[string]int
var a map[int]string
var a map[string]map[string]string等;
3)map在声明时并不会分配内存,用make初始化后,才会分配内存,然后才能进行赋值和使用;
4)golang中的map,默认是无序的状态,且没有专门的针对排序的方法;key不能重复,但value可以重复;
5)map的三种使用方式:
先声明,再make分配内存空间,再使用; 声明后直接make分配内存空间,再使用; 声明后直接赋值;
package main
import "fmt"
// map在声明时并不会分配内存,用make初始化后,才会分配内存,然后才能进行赋值和使用;
func main() {
// map的三种使用方式:
// 方式一:先声明,再make分配内存空间,再使用
var map1 map[int]string
map1 = make(map[int]string, 5)
// 方式二:声明后直接make分配内存空间,再使用
map2 := make(map[int]string)
for i := 1; i <= 5; i++ {
map2[i] = string('A' + byte(i))
}
// 方式三:声明后直接赋值
map3 := map[int]string{"1":"A", "2":"B", "3":"C", "4":"D", "5":"E"}
// map的遍历:
for key, value := range map3 {
fmt.Printf("map3[%d]=%q\n", key, value)
}
fmt.Println(map3)
}
map[string]map[string]string的使用:
package main
import "fmt"
func main() {
studentMap := make(map[string]map[string]string)
studentMap["01"] = make(map[string]string, 3)
studentMap["01"]["name"] = "tom"
studentMap["01"]["sex"] = "male"
studentMap["01"]["address"] = "shanxi"
studentMap["02"] = make(map[string]string, 3)
studentMap["02"]["name"] = "jary"
studentMap["02"]["sex"] = "female"
studentMap["02"]["address"] = "shanghai"
fmt.Println(studentMap)
// map[string]map[string]string的遍历:
for _, address := range studentMap {
for key, value := range address {
fmt.Printf("[%q]=%q ", key, value)
}
fmt.Printf("\n")
}
}
6)map的增删改查操作:
map的增加和更新:map[key]=value;如果key不存在则是增加,否则则是更新。 map的删除:使用内置函数delete(map,key);如果可以存在则删除,否则不操作也不报错。 补充说明:如果要删除map中所有的key,1)可以通过遍历逐个删除;2)make一个新的map并直接赋值给map(map=make(...)),即这会原来的map成为垃圾被GC回收。
map的查找:1)val,ok=map[key]; 2)通过for-range遍历不同的key来实现查找的目的。
package main
import "fmt"
import "unsafe"
func SearchMap(map_ map[string]string, key_search string) {
fmt.Printf("%p\n", &map_)
for key, value := range map_ {
if key == key_search {
fmt.Printf("[%q]=%q\n", key, value)
}
}
}
// map的增、删、改、查
func main() {
map_ := make(map[string]string, 5)
map_["name"] = "SGY" // 增
map_["sex"] = "male"
map_["address"] = "shanxi"
map_["phone"] = "8888"
fmt.Println(map_)
fmt.Printf("%d\n", unsafe.Sizeof(map_))
fmt.Printf("%d\n", unsafe.Sizeof(map_["sex"]))
map_["name"] = "Guangyuan" // 改
fmt.Println(map_)
fmt.Printf("%p\n", &map_)
SearchMap(map_, "address") // 查
value, ok := map_["address"]
if ok {
fmt.Printf("[%q]=%q\n", "address", value)
} else {
fmt.Printf("[%q]不存在", "address")
}
// delete(map, key)如果key存在,则删除key-value;不存在,则不操作,也不会报错;
delete(map_, "phone") // 删
fmt.Println(map_)
fmt.Printf("map_的key-value的对数,%d\n", len(map_))
// 没有专门的方法能直接删除map中的所有的key,有两种解决方法:
// 1)只能通过遍历逐个删除;
for key, _ := range map_ {
delete(map_, key)
}
fmt.Println(map_)
// 2)重新给map分配空间,使原来的内存空间的引用变成零,即会被GC当作垃圾回收;
map_ = make(map[string]string)
fmt.Println(map_)
}
map切片
切片的数据类型如果是map,则称为slice of map(map切片),这样使用map的个数就可以动态变化了。
package main
import "fmt"
import "strconv"
// 切片的数据类型如果是map,则称为slice of map(map切片),这样使用map的个数就可以动态变化了
func main() {
var map_slice []map[string]string;
map_slice = make([]map[string]string, 5)
//map_ := make(map[string]string, 10)
var map_ map[string]string
for i := 0; i < len(map_slice); i++ {
map_ = make(map[string]string, 10)
if map_slice[i] == nil {
for j := 0; j < 10; j++ {
map_[strconv.Itoa(j)] = strconv.Itoa(j * 10)
}
map_slice[i] = map_
fmt.Println(map_)
}
}
fmt.Println(map_slice)
// 用append给map切片添加元素,来动态的增加map切片的长度
map_ = map[string]string{
"...":"...///",
"..":"..///",
".":".///",
}
map_slice = append(map_slice, map_)
fmt.Println(map_slice)
fmt.Println("map_slice的切片长度:", len(map_slice))
}
map排序
golang中map默认是无序的,且每次遍历的输出顺序都不同;map的排序,只能先将key进行排序,再根据key值遍历输出。
package main
import (
"fmt"
"strconv"
"sort"
)
// 当切片作为形参时,切片只能在函数内部修改值,不能直接添加值
func Insert_Sort(map_ map[int]string) []int {
var key_ []int
for key, _ := range map_ {
key_ = append(key_, key)
sort_len := len(key_)
if sort_len == 1 {
continue
} else {
left := 0
right := sort_len - 2
for {
if (right - left) > 1 {
middle := (left + right) / 2
if key < key_[middle] {
right = middle
continue
} else if key > key_[middle] {
left = middle
continue
} else {
for i := sort_len - 2; i >= middle; i-- {
key_[i + 1] = key_[i]
}
key_[middle] = key
break
}
} else {
if key >= key_[left] && key <= key_[right] {
for i := sort_len - 2; i >= right; i-- {
key_[i + 1] = key_[i]
}
key_[right] = key
} else if key > key_[right]{
for i := sort_len - 2; i >= right + 1; i-- {
key_[i + 1] = key_[i]
}
key_[right + 1] = key
} else {
for i := sort_len - 2; i >= left; i-- {
key_[i + 1] = key_[i]
}
key_[left] = key
}
break
}
}
}
}
return key_
}
func GetSortedValue(value *[]string, map_ map[int]string, key_ []int) {
for _, key := range key_ {
*value = append((*value), map_[key])
}
}
// golang中map默认是无序的,且每次遍历的输出顺序都不同;
//map的排序,只能先将key进行排序,再根据key值遍历输出;
func main() {
map_ := make(map[int]string, 20)
for i := 0; i < 20; i++ {
map_[i] = strconv.Itoa(i * 10)
}
fmt.Println(map_)
// 方式一:先将key放入切片中, 并对切片进行排序;最后按照切片中的key查找相应的value
var key_1 []int
for key, _ := range map_ {
key_1 = append(key_1, key)
}
sort.Ints(key_1)
fmt.Println(key_1)
var value_1 []string
GetSortedValue(&value_1, map_, key_1)
fmt.Println(value_1)
// 方式二:将key逐个放入key_中,并利用插排,完成排序;最后按照切片中的key查找相应的value
var key_2 []int = Insert_Sort(map_)
fmt.Println(key_2)
var value_2 []string
GetSortedValue(&value_2, map_, key_2)
fmt.Println(value_2)
}
8.range
range是一种迭代遍历手段,可操作的类型有数组、切片、string、map、channel等
遍历数组
myArray := [3]int{1, 2, 3}
for i, ele := range myArray {
fmt.Printf("index:%d,element:%d\n", i, ele)
fmt.Printf("index:%d,element:%d\n", i, myArray[i])
}
遍历slice
mySlice := []string{"I", "am", "peachesTao"}
for i, ele := range mySlice {
fmt.Printf("index:%d,element:%s\n", i, ele)
fmt.Printf("index:%d,element:%s\n", i, mySlice[i])
}
遍历string
s:="peachesTao"
for i,item := range s {
fmt.Println(string(item))
fmt.Printf("index:%d,element:%s\n", i, string(s[i]))
}
遍历map
myMap := map[int]string{1:"语文",2:"数学",3:"英语"}
for key,value := range myMap {
fmt.Printf("key:%d,value:%s\n", key, value)
fmt.Printf("key:%d,value:%s\n", key, myMap[key])
}
遍历channel
myChannel := make(chan int)
go func() {
for i:=0;i<10;i++{
time.Sleep(time.Second)
myChannel <- i
}
}()
go func() {
for c := range myChannel {
fmt.Printf("value:%d\n", c)
}
}()
for range 和 for的区别
- for range可以直接访问目标对象中的元素,而for必须通过下标访问
- for frange可以访问map、channel对象,而for不可以
所以在range中修改时,需要通过下标访问slice中的元素对其赋值修改。
9.函数、
// 函数:实现某一功能的代码块
func 函数名(形参列表) (返回值列表) {
执行语句
return 返回值列表
}
打包/引包的方法:
-
一般包名和.go文件名相同;且package ***打包操作,必须在.go文件的开头;
-
为了让包中的函数/变量/常量在其他包中,能够正常访问,需要在声明和定义时将函数/变量/常量的首字母大写;
-
同一个包下,不能有同名的函数/变量/常量,否则会报重复定义的错误;
utils.go
package Utils // package cal
import "fmt"
func ArithmeticOperate(num1 float64, num2 float64, operator byte) float64 {
var result float64
switch operator {
case '+':
result = num1 + num2
case '-':
result = num1 - num2
case '*':
result = num1 * num2
case '/':
result = num1 / num2
default:
fmt.Printf("Operator Error")
}
return result
}
main.go
package main
import "fmt"
import "Go_Code/Function/ArithmeticOperate/utils"
// import包时,路径是从$GOPATH的src下开始的,不需要再带src,编译器会自动从src开始引入
func main() {
var num1 float64 = 4
var num2 float64 = 3
var operator byte = '/'
res := utils.ArithmeticOperate(num1, num2, operator)
// res := cal.ArithmeticOperate(num1, num2, operator)
fmt.Printf("%.4f", res)
}
注意:
1)函数的形参列表、返回值列表,都可以是多个的;
2)形参列表 和 返回值列表的数据类型,可以是值类型 和 引用类型;
3)函数的首字母大写,认为是可以挎包使用的,即public;首字母小写,认为是private,只能在本包中使用;
4)基本数据类型 和 数组,默认都是 值传递 的,即进行值拷贝。如果想用引用传递,则必须传入变量的地址,并在函数内通过指针的方式操作变量,类似于引用;5)Go语言不支持函数重载;
6)在Go中,函数也是一种数据类型,可以赋值给一个变量,则该变量就是一个函数类型的变量,通过该变量可以对函数进行调用;函数也可以作为形参;支持对函数返回值命名;
package main
import "fmt"
// 支持对函数返回值命名
func GetSum2(n1 int, n2 int) (n int) {
n = n1 + n2
return
}
func GetSum1(n1 int, n2 int) int {
n := n1 + n2
return n
}
// 函数也可以作为形参
func GetSum_(myAddfunc func(int, int) int, n1 int, n2 int) int {
return myAddfunc(n1, n2)
}
func main() {
// Go中,**函数也是一种数据类型**,可以赋值给一个变量,则该变量就是一个函数类型的变量
FuncTypeVar := GetSum2
fmt.Printf("FuncTypeVar type is %T\n", FuncTypeVar) // func(int, int) int
// 通过该变量可以对函数进行调用
result := FuncTypeVar(10, 11)
fmt.Printf("result = %d\n", result)
// 函数也可以作为形参
result2 := GetSum_(FuncTypeVar, 10, 11)
fmt.Printf("result2 = %d\n", result2)
}
为了简化数据类型定义,Go支持自定义数据类型
基本语法:type 自定义数据类型 数据类型 // 相当于一个别名 举例:type mySum func(int, int) int 8)占位符_,可以在调用有多个返回值的函数时,忽略某个返回值;
9)Go支持可变参数:可变参数要放在,形参的最后。
其中,args是slice切片,可以通过下标args[index]访问到
// 支持0 ~ 多个参数
func (args... int) int {
}
// 支持1 ~ 多个参数
func (n1 int, args... int) int {
}
package main
import "fmt"
func sum(n1 int, args... int) int {
sum := n1
for i := 0; i < len(args); i++ {
sum += args[i] // 表示依次取出args切片中的各个元素
}
return sum
}
func main() {
res := sum(1, 2, 3, 4)
fmt.Printf("result = %d\n", res)
}
init函数
每个.go文件,都可以包含一个init函数,该函数会在main函数执行之前,被Go运行框架调用,也就是说init会在main函数前被调用,通常用来完成初始化的工作。
如果一个函数中同时含有全局变量的定义、init()函数、main()函数,则执行的顺序是,全局变量的定义 -> init()函数 -> main()函数。
package main
import "fmt"
// init()函数,可以用来完成一些初始化的工作
var age = test()
func test() int {
fmt.Println("test()")
return 90
}
func init() {
fmt.Println("..... Init() .....")
}
func main() {
fmt.Println("..... main() .....", age)
}
Go支持匿名函数,不多展开。
10.指针
&x表达式(取x变量的内存地址)将产生一个指向该整数变量的指针
*p 对应一个变量,所以该表达式也可以出现在赋值语句的左边,表示更新指针所指向的变量的值、
基本数据类型(int系列、float系列、bool、string、数组和结构体struct)中,变量存的是值,也叫值类型。值类型都有对应的指针类型,形式为数据类型。 获取变量的地址用&; 指针变量ptr存的是一个地址,且该地址指向的空间存的是值,比如var ptr int = &num; 获取指针的值,使用,比如ptr;