200字范文,内容丰富有趣,生活中的好帮手!
200字范文 > go语言基础(二):匿名函数 数组 随机数 切片 字符串

go语言基础(二):匿名函数 数组 随机数 切片 字符串

时间:2022-06-07 09:05:36

相关推荐

go语言基础(二):匿名函数 数组 随机数 切片 字符串

go语言基础(二):匿名函数、数组、随机数、切片、字符串

1. 匿名函数

匿名函数的使用常用的有两种情况

定义并调用匿名函数

func main() {// 匿名函数的定义// func后面的第一个括号代表函数的形参列表// func后面的第一个括号后可以跟返回值// 在func后面的反括号后还要再加上一个括号,代表函数的调用,里面要写函数的实参func() {fmt.Println("hello world")}()func(a int, b int) {fmt.Println(a+b)}(10, 20)}

定义匿名函数,将该函数赋值给函数变量

type FUNCTYPE func(int)func main() {var f FUNCTYPE// 可以使用 := 进行自动类型推导,那么久不用定义函数变量f = func(int){fmt.Println("this is func(int)")}f(10)}

2. 数组

数组的定义:var 数组名 [数组元素个数]数据类型数组定义时的初始化:var 数组名 [数组元素个数]数据类型 = [数组元素个数]数据类型{数值,数值,...}可以使用自动推导直接定义出一个数组

其他的数组特性就不用过多赘述了,和C语言的相同

func main() {// 数组的定义// var 数组名 [数组元素个数]数据类型var arr [10]int = [10]int{1, 2, 3, 4}// 默认数组的初始值为0fmt.Println(arr)// 使用自动推导arr1 := [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}fmt.Printf("%T\n", arr1)fmt.Println(arr1)fmt.Println(len(arr1)) // 计算数组元素个数// 遍历数组for i:=0; i<len(arr1); i++ {fmt.Printf("%d ", arr1[i])}fmt.Println()// 范围遍历数组for i, v := range arr1 {fmt.Println("index =", i, "value =", v)}// 这种初始化方式根据后面的初始化的元素个数决定数组大小arr2 := [...]int{1, 2, 3, 4, 5}fmt.Println(arr2)}// 结果[1 2 3 4 0 0 0 0 0 0][10]int[1 2 3 4 5 6 7 8 9 10]101 2 3 4 5 6 7 8 9 10 index = 0 value = 1index = 1 value = 2index = 2 value = 3index = 3 value = 4index = 4 value = 5index = 5 value = 6index = 6 value = 7index = 7 value = 8index = 8 value = 9index = 9 value = 10

范围遍历的语法:(可以使用匿名变量做占位_

for 下标, 值 := range 数组名 {语句}

数组的存储

与C语言区分开,数组的地址要使用&数组名来访问,C语言是数组名直接就是数组的首地址,没有这个&

数组的地址和数组中的元素的首地址是相同的,这一点和C语言是相同的

func main() {arr := [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}// 打印数组的元素地址fmt.Printf("%p\n", &arr)// 打印数组中的元素的地址for i, _ := range arr {fmt.Printf("%p\n", &arr[i])}}// 结果0xc400000xc400000xc400080xc400100xc400180xc400200xc400280xc400300xc400380xc400400xc40048

以上代码中打印出来了数组的地址和数组中的每个元素的地址,由此可见,数组的地址和数组中首元素的地址是相同的,而且在go语言中64位机上int是占8个字节,所以每个数组元素的地址差8。(32位机上int占4字节)

数组的初始化

可以使用一个数组给另外一个数组进行初始化赋值

func main() {arr := [5]int{1, 2, 3, 4, 5}var arr1 [5]intarr1 = arrfmt.Println(arr)fmt.Println(arr1)}

此时这两个数组是独立的两个数组。

注意:在进行数组的如上赋值操作时必须有相同的元素个数和相同的数据类型

如果想要将两个不同类型的数组进行赋值,首先不能使用数组的类型转换,这样是不支持的,可以使用for循环进行遍历赋值

数组的逆置练习

在C语言中的想法是使用一个while循环,双指针来进行交换,而go语言中首先没有while,那么在go语言中它的for就可以达成while的操作。

for 条件表达式 {语句}

func main() {arr := [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}fmt.Println(arr)i := 0j := len(arr)-1// 这个for就是类似于C语言的whilefor i < j {arr[i], arr[j] = arr[j], arr[i]i++j--}fmt.Println(arr)}

3. 随机数

和C语言一样,如果需要定义真正的随机数,需要设置随机数种子,需要导入两个包

import ("math/rand""time")rand.Seed(time.Now().UnixNano())

生成随机数

rand.Intn(生成的数字模除的值)

func main() {// 设置随机数种子rand.Seed(time.Now().UnixNano())// 生成一个随机数// 生成随机数的函数的参数表示num := rand.Intn(100)fmt.Println(num)for i:=0; i<10; i++ {fmt.Println(rand.Intn(100))}}

4. 二维数组

语法:var 数组名 [行的个数][列的个数]数据类型

func main() {// var arr [行的个数][列的个数]数据类型var arr [2][3] int// 数组的初始化赋值// 二维数组在赋值的时候必须要写每一个行的大括号,如果不写是报错的arr = [2][3]int{{1, 2, 3},{4, 5, 6}, // 如果这样换行写的话,这里必须要写一个逗号才能将下面的大括号换行写,比较好看}// len(二维数组) 表示二维数组的行的个数fmt.Println(len(arr))fmt.Println(len(arr[0]))// 遍历二维数组for i:=0; i<len(arr); i++ {for j:=0; j<len(arr[0]); j++ {fmt.Print(arr[i][j], " ")}fmt.Println()}}

5. 切片

数组中的元素个数在定义的时候必须是一个常量或者是一个常量表达式

var count int = 10var arr [count]int

以上代码在语法检查不会报错,在编译时报错,显示变量无法用来定义元素个数

数组无法进行元素的扩展

通过切片可以解决以上问题

5.1 切片的定义及添加数据

定义一个空切片:var 切片名 []数据类型定义出来的是一个空切片,不能直接用来进行下标访问,需要添加数据后才能使用 为切片添加数据:切片名 = append(切片名, 元素1, 元素2, ...)

func main() {// 创建一个空切片var slice []int// 为切片添加数据slice = append(slice, 123)slice = append(slice, 456, 789)fmt.Println(slice)}

定义切片并初始化切片的大小 语法1:var 切片名 []数据类型 = make([]数据类型, 元素个数),此时元素个数与容量相同语法2:var 切片名 []数据类型 = make([]数据类型, 元素个数, 容量大小),长度要小于等于容量

func main() {// 在创建切片的时候直接指定切片的大小// 语法:var 切片名 []数据类型 = make([]数据类型, 元素个数)// 此时会将切片初始化元素个数为指定个数并初始化数据为0var slice []int = make([]int, 10)slice[0] = 123slice[1] = 234fmt.Println(slice)// 打印切片元素个数fmt.Println(len(slice))// 打印切片容量fmt.Println(cap(slice))slice = append(slice, 123, 456)// 打印切片元素个数fmt.Println(len(slice))// 打印切片容量// 容量扩充为上一次的两倍fmt.Println(cap(slice))}// 结果[123 234 0 0 0 0 0 0 0 0]1020

注意:切片的扩容步骤:寻找足够的空间,将原数据拷贝到新的空间,释放原空间,随后再添加数据。因为扩容的步骤比较繁琐且有很多的时间开销,所以每次在容量满时不会仅仅扩容一个元素的大小,而是会预留出大小。 使用自动推导创建切片

slice := []int{1, 2, 3, 4, 5}fmt.Println(len(slice))fmt.Println(cap(slice))// 以上两个结果都是5slice := make([]int, 5)fmt.Println(len(slice))fmt.Println(cap(slice))// 以上两个结果都是5

注意:不要使用cap进行打印操作

5.2 切片的打印

使用下标或者for-range即可

for i, v := range slice {fmt.Println("index =", i, "value =", v)}

5.3 切片的拷贝

提到切片的拷贝首先要说切片的实际内容

func main() {slice := []int{1, 2, 3, 4, 5}fmt.Println(slice) fmt.Println(unsafe.Sizeof(slice)) // 输出24}

slice的大小是24,原因是切片实际内容并不是其中的数据,而是它维护了三个变量,分别是切片的大小、切片的容量和存储数据的地址,真正的数据是存储在这个指向的地址中的。

那么所以在发生拷贝时,进行的浅拷贝,也就是说拷贝前后两个变量指向的是同一个切片,修改了其中的一个切片,另一个切片的值也会一并更改

func main() {slice := []int{1, 2, 3, 4, 5}fmt.Println(slice)fmt.Println(unsafe.Sizeof(slice)) // 输出24si := slicefmt.Println(si) // [1 2 3 4 5]slice[0] = 123fmt.Println(si) // [123 2 3 4 5]}

copy()函数会使发生深拷贝,此时更改一个的值不会更改另一个。

copy()函数:参1 dest,参2 source

// 深拷贝sli := make([]int, 5)copy(sli, slice)fmt.Println(sli)slice[0] = 234fmt.Println(sli)fmt.Println(slice)

5.4 切片的截取

切片的截取都是在原切片的基础上进行操作的,所以切片截取后的地址和原切片相同,修改截取前的原切片也会对截取后的结果造成影响。

切片截取的语法:目标值 := 原切片[开始下标:结束下标],截取的内容不包括结束下标元素

func main() {slice := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}s1 := slice[0:5]fmt.Println(s1)fmt.Printf("%p\n", slice)fmt.Printf("%p\n", s1)}// 结果[1 2 3 4 5]0xc4200160a00xc4200160a0

切片的截取的写法

slice[起始位置:] // 从起始位置到切片的结尾slice[:] // 切片的全部slice[:结束位置] // 从0开始到结束位置,不包括结束位置

切片截取后的容量

切片的截取公式:slice[low:high:max]容量:max-low长度:high-low当max没有指定的时候,默认是切片的容量最大容量要大于结束位置

s2 := slice[2:5:8]fmt.Println(s2) // [3, 4, 5]fmt.Println(len(s2)) // 3fmt.Println(cap(s2)) // 6

5.5 切片做函数参数

func test1(slice []int) {fmt.Println(slice)}func test2(slice ...int){// 可以使用不定参进行传递切片,因为不定参的原型就是切片fmt.Println(slice)}func main() {slice := []int{9, 8, 7, 6, 5, 4, 3, 2, 1, 0}test1(slice)test2(slice...) // slice...的原型是切片}

尽量不要写不定参,不易读

切片做函数参数传参发生的是值传递,传递的是实际存储数据的地址,所以传进函数和外面的是同一个切片,形参可以修改形参的值

5.6 切片的扩容

一般情况,当切片元素个数要超过切片容量时,每次扩容都是上一次的倍数(2倍)特殊情况 当一次性append多个元素导致切片元素大于容量时,切片容量是添加元素后的切片总元素个数的向上取偶当容量超过1024时,扩容至原先的1.25倍

5.7 切片的append函数参数传递

若想使一个切片添加到另一个切片的后面,可以使用append函数,但是参数的传递要注意,不能直接使用切片名,要使用不定参传递,因为append函数的第二个参数需要的是元素而不是一个切片。

func main() {slice1 := []int{1, 2, 3, 4, 5}slice2 := []int{6, 7 ,8, 9, 10}for _, v := range slice2 {slice1 = append(slice1, v)}fmt.Println(slice1)// 使用append函数slice1 = append(slice1, slice2...) // 要使用不定参的方式传递第二个参数fmt.Println(slice1)}

5.8 字符串与字符切片相互转换

使用[]byte()对字符串进行强制转换,可以得到一个字符切片,打印这个切片时默认元素是字符的ASCII码值

使用string()可以对字符切片强制转换为字符串

func main() {str := "hello"slice := []byte(str)fmt.Println(slice)for _, v := range slice {fmt.Printf("%c", v)}fmt.Println()str2 := string(slice)fmt.Println(str2)}// 结果[104 101 108 108 111]hellohello

6. 字符串

字符串在末尾有一个\0作为结束标记,算在字符串的实际长度中使用len()对字符串求长度,计算的是\0之前的长度

6.1 字符串与汉字

汉字占3个字节,汉字是使用ASCII码的128-256直接实现的

func main() {str := "我爱学习"for i:=0; i<len(str); i++ {fmt.Println(str[i])}}// 结果230136145231136177229173166228185160

以上结果,每三个数字是一个str中的汉字

6.2 字符串的操作函数

字符串包含

str1 := "我爱学习"str2 := "学习"// strings.Contains(str, substr) 返回str1中是否包含str2// 用作于模糊查找fmt.Println(strings.Contains(str1, str2))

字符串拼接

slice := []string{"我", "爱", "学", "习"}// 字符串拼接,将一个字符串切片中的数据使用参数2的字符串作为连接符拼接成一个字符串str := strings.Join(slice, "")fmt.Println(str)

字符串查找

str1 := "hello"str2 := "llo"// strings.Index() 用于找到参数2在参数1中出现的位置,如果不存在则返回-1num := strings.Index(str1, str2)fmt.Println(num)str3 := "abc"fmt.Println(strings.Index(str1, str3))

字符串重复

str1 := "我爱学习"// 将字符串重复n次ch := strings.Repeat(str1, 3)fmt.Println(ch)

字符串替换

str := "hello"// 字符串替换函数,参1是要操作的字符串,参2是要被替换的字符,参3是替换成为的字符,参4是替换的次数// 默认从左向右进行搜素str = strings.Replace(str, "l", "s", 2)fmt.Println(str)

字符串收尾去除指定字符

str := "ab c, cde "// strings.Trim() 用于去掉参1字符串的头尾的参2字符串中的字符,这个字符可以是多个,字符串中间的该字符不会被去掉ch := strings.Trim(str, " ")fmt.Println(ch)str2 := "======a=====aaaaaah=ello=====aaaaaa===="ch2 := strings.Trim(str2, "a=")fmt.Println(ch2)

字符串去空格

str := "are u ok"// strings.Fields() 用于去掉字符串中的空格,并存在一个切片中slice := strings.Fields(str)for _, v := range slice {fmt.Println(v)}

字符串分割

str := "123-456-789"// strings.Split() 用于将参1的字符串通过参2进行分割,分割的结果是一个字符串切片ch := strings.Split(str, "-")fmt.Println(ch)

6.3 字符串类型转换

Format系列函数将其他类型数据转换为字符串类型

func main() {// 从其他类型转换为字符串类型// 布尔类型转为字符串b := truestr := strconv.FormatBool(b)fmt.Println(str)fmt.Printf("%q\n", str) // %q 的占位符打印带双引号// 整型转换为字符串str1 := strconv.FormatInt(10, 2) // 参数1是一个int64类型的数据,转换为参数2的进制后转换为字符串// PS: 最高36进制,最低2进制fmt.Println(str1)str2 := strconv.Itoa(123445)fmt.Println(str2)// 将浮点型转换为字符串// 参1:要进行转换的数据,参2:表示数据的类型是一个浮点型,参3:表示处理过后小数点后面留几位,参4:处理的方式(64位和32位两种,其他报错)// 其中参3的小数保留位数会四舍五入str3 := strconv.FormatFloat(3.14, 'f', 5, 64)fmt.Println(str3)}

Parse系列函数将字符串类型转换为其他类型

func main() {// 将字符串类型转换为其他类型// 字符串转布尔类型// strconv.ParseBool函数有两个返回值,一个是返回的对应类型的值,一个是err错误值// err默认是nil, 当err不是nil的时候,说明出错了,另一个返回值也就不用接收了bl, err := strconv.ParseBool("true")if err != nil {fmt.Println(err)}fmt.Println(bl)fmt.Printf("%T\n", bl)// 字符串转整型// strconv.ParseInt 参数1:待转换的值,参数2:把参数1当成什么进制来进行转换,参数3:指定结果必须能无溢出赋值的整数类型// 返回值是int64类型的// 注意:不能像C语言一样如果出现字母解析字母前的数字,在这里会直接设置err,返回值是0v, _ := strconv.ParseInt("123", 10, 64)fmt.Println(v)fmt.Println(unsafe.Sizeof(v))// 字符串转整型i, _ := strconv.Atoi("123456")fmt.Println(i)// 字符串转浮点型f, _ := strconv.ParseFloat("123.456", 64)fmt.Println(f)}

6.4 其他类型转字符切片

使用append系列函数

func main() {// 其他类型转换为字符切片var slice []byte// append系列函数会向切片中进行追加,不会覆盖slice = strconv.AppendBool(slice, true)slice = strconv.AppendInt(slice, 123, 10)slice = strconv.AppendFloat(slice, 3.14, 'f', 6, 64)// 这里的转换字符切片比直接类型转换多一对""slice = strconv.AppendQuote(slice, "hello")fmt.Println(string(slice))}

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。