Go学习记录1 - 20220322

工作项目原因,最近开始学习Go语言,自学看书为主。这本比较经典:Go语言圣经 。之前有些C语言基础,Go和C\C++一样有指针这种东西,理解起来不算太难。
部门的TL出了些自学练习题,第一部分覆盖了《Go语言圣经》基本语法、程序结构、基础数据类型、复合数据类型、函数、方法、接口等章节内容。题目挺好的,通过做题,熟悉Go,快速上手。翻翻资料、看看书、百度查下,也花了不少时间,把练习做出来了。骐骥一跃,不能十步,驽马十驾,功在不舍。
下面记录下这些题和我的解答。

  1. 字符串字面量和原始字符串字面量有什么区别?请用代码实现以下要求:
    (1)用一条语句打印出两行文本;
    (2)打印出Windows系统的文件路径C:\go
    (3)在一条语句中用原始字符串字面量打印出多行文本;
/*
一个原生的字符串面值形式是`...`,使用反引号代替双引号。
在原生的字符串面值中,没有转义操作;全部的内容都是字面的意思,包含退格和换行,因此一个程序中的原生字符串面值可能跨越多行。
*/

func exercise1() {
	s1 := `Hello
World`
	fmt.Println(s1)

	fmt.Println(`C:\go`)
	fmt.Println("C:\\go")

	s2 := "Hello\nWorld"
	fmt.Println(s2)
}

2.打印出字符串“Hello”中的每一个字符,并使每个字符独占一行。尝试修改字符串中的某个字符,测试下是否能修改成功。

func exercise2() {
	s := "Hello"
//修改字符串中某个字符失败
	// s[0] = 'h'
	for i := 0; i < len(s); i++ {
		fmt.Println(string(s[i]))
	}
}

在这里插入图片描述
3. 请实现以下类型转换:
(1)把整数10转成字符串;
(2)把字符串“10”转成整数;
(3)把布尔值true转成字符串;
(4)把字符串“true”转成布尔值;

func exercise3() {
	// 整数转为字符串
	num1 := 10
	s1 := strconv.Itoa(num1)
	fmt.Println(s1)
	fmt.Sprintln(num1)

	//字符串转为整数
	num2, error := strconv.Atoi("12")
	if error == nil {
		fmt.Println(num2)
	}

	//布尔值转为字符串
	fmt.Println(strconv.FormatBool(true))

	//字符串转为布尔值
	b, error := strconv.ParseBool("true")
	if error == nil {
		fmt.Println(b)
	}
}
  1. 初始化一个字符串数组,并用两种方式遍历数组中的元素。考虑下两种方式各有什么优点?
func exercise4() {
	array := [...]string{"ZTE", "Zte", "zte"}

	//使用range遍历
	for _, str := range array {
		fmt.Print(str + " ")
	}
	fmt.Println()
	//使用索引遍历
	for i := 0; i < len(array); i++ {
		fmt.Print(array[i] + " ")
	}
	fmt.Println()

//使用索引遍历可以从任何合法位置开始,向左/向右遍历到任何合法位置结束,更加灵活
	//range遍历不需要考虑索引范围和操作索引,写法更简单
}

5.定义并初始化字符串数组arrayA,把arrayA赋值给新的变量arrayB,修改arrayB中的元素,检查arrayA是否会随之改变?把arrayA作为参数传递给函数,在函数中修改arrayA的元素,检查函数执行后arrayA是否改变。考虑下Go数组和Java数组有何不同?

func exercise5() {
	arrayA := [...]string{"ZTE", "Zte", "zte"}
	arrayB := arrayA

	//修改赋值后的新数组arrayB中元素,原数组arrayA元素的值不变
	arrayB[0] = "Hello"
	fmt.Println("arrayA[0] is " + arrayA[0] + " arrayB[0] is " + arrayB[0])
	fmt.Println(arrayB[0] == arrayA[0])

	//arrayA作为参数传给函数,在函数中修改arrayA的值
	setHello(arrayA, 0)
	fmt.Println("arrayA[0] is " + arrayA[0] + " arrayB[0] is " + arrayB[0])
	fmt.Println(arrayB[0] == arrayA[0])
	//Go中的数组赋值是值传递,相当于创建了一个元素相同的新数组,Java中的数组赋值,是赋值的引用。
	//Go中的数组是值传递,除非传递数组指针,否则函数不能修改原数组的值。Java中的数组是引用传递。
}

// 传入指针
// func setHello(arr *[3]string, index int) {
// 	arr[index] = "Hello"
// }

func setHello(arr [3]string, index int) {
	arr[index] = "Hello"
}

在这里插入图片描述
6. 定义并初始化字符串数组arrayA,创建切片sliceA,包含arrayA的所有元素,修改sliceA中的元素,检查arrayA是否会随之改变。把sliceA作为参数传递给函数,在函数中修改sliceA的元素,检查函数执行后arrayA是否改变。考虑下切片和数组有何不同?

func exercise6() {
	arrayA := [...]string{"ZTE", "Zte", "zte"}
	sliceA := arrayA[:]
	sliceA[0] = "Hello"
	fmt.Println("arrayA[0] is " + arrayA[0] + " sliceA[0] is " + sliceA[0])
	fmt.Println(sliceA[0] == arrayA[0])
	setHello2(sliceA, 0)
	fmt.Println("arrayA[0] is " + arrayA[0] + " sliceA[0] is " + sliceA[0])
	fmt.Println(sliceA[0] == arrayA[0])
	//slice是可变长度的,数组是固定长度的。slice和数组的字面值语法很类似,它们都是用花括弧包含一系列的初始化元素,但是对于slice并没有指明序列的长度。
	//因为slice值包含指向第一个slice元素的指针,因此向函数传递slice将允许在函数内部修改底层数组的元素。换句话说,复制一个slice只是对底层的数组创建了一个新的slice别名。
}

func setHello2(slice []string, index int) {
	slice[index] = "ZTE"
}

在这里插入图片描述
7.请编写一个程序,循环若干次将元素追加至切片,并在切片容量发生变化时打印出切片的容量。请判断append函数在底层数组的空间被填满之后,是否总会将数组的容量增加一倍?

func exercise7() {
	slice := []int{}
	cap1 := cap(slice)

	for i := 0; i < 10000; i++ {
		slice = append(slice, i)
		cap2 := cap(slice)
		if cap2 != cap1 {
			fmt.Printf("The currrent cap is %d\n", cap2)
			cap1 = cap2
		}
	}
}

//append在底层数组被填满时,并不总是容量增加一倍。具体机制是,当容量小于1024时,当需要的容量大于原切片容量,切片容量会增加1倍。
//当容量大于或等于1024,当需要的容量大于原切片容量,切片容量会增加0.25倍,是原来的1.25倍,也可能大于1.25倍。

//打印:
在这里插入图片描述
8.切片append操作引起底层数组容量扩充,扩充后的数组和原数组是否是同一个数组?写程序证明你的猜测。
如果容量扩充会产生新数组,有什么方法可以尽量避免append操作引起底层数组容量扩充,从而避免额外的内存分配和数组复制操作。

func exercise8() {
	slice := []int{}
	slice = append(slice, 1, 2)
	slice2 := append(slice, 3, 4)
	slice2[0] = 9
	fmt.Printf("slice[0] is: %d slice2[0] is: %d\n", slice[0], slice2[0])
	fmt.Println(slice[0] == slice2[0])
	//打印
	//slice[0] is: 1 slice2[0] is:9
	//false
	//底层数组扩容后,扩充后的数组和原数组不是同一个数组
	//预指定slice的容量,容量足够大,可以减少扩容次数。比如设置slice的初始长度为1,容量为6。
	slice3 := make([]int, 1, 6)
	slice3 = append(slice3, 1, 2, 3, 4, 5, 6)
	fmt.Println(slice3)
}

9.对一个字符串数组进行分组,将长度相同的字符串分为一组,并打印分组结果。

func exercise9() {
	strarr := [...]string{"ZTE", "Hello", "Zte", "World", "zte", "today", "good", "go", "road", "ok"}
	strgroup := make(map[int][]string)
	for _, str := range strarr {
		strgroup[len(str)] = append(strgroup[len(str)], str)
	}
	lens := make([]int, 0, len(strgroup))
	for length := range strgroup {
		lens = append(lens, length)
	}
	sort.Ints(lens)
	for _, len := range lens {
		fmt.Printf("Words with length %d : %v\n", len, strgroup[len])
		// for _, word := range strgroup[len] {
		// 	fmt.Print(word + " ")
		// }
		// fmt.Println()
	}
}

在这里插入图片描述

10.定义并初始化结构变量structA,把structA赋值给新的变量structB,修改structB中的数据,检查structA是否会随之改变。把structA作为参数传递给函数,在函数中修改structA的数据,检查函数执行后structA是否改变。考虑下和Java的对象有何不同?

type CPN struct {
	Rack  int
	Shelf int
	Slot  int
	Port  int
}

func exercise10() {
	cpn1 := CPN{Rack: 1, Shelf: 1, Slot: 1, Port: 1}
	cpn2 := cpn1
	cpn2.Port = 2
	fmt.Printf("cpn1.Port == cpn2.Port is: %t\n", cpn1.Port == cpn2.Port)
	setPort(&cpn1, 3)
	fmt.Printf("cpn1.Port = %d\n", cpn1.Port)
}

func setPort(cpn *CPN, portNo int) {
	cpn.Port = portNo
}
//Go的结构体是值传递,Java对象是引用传递。
// 如果要在函数内部修改结构体成员的话,用指针传入是必须的;因为在Go语言中,所有的函数参数都是值拷贝传入的,函数参数将不再是函数调用时的原始变量。

在这里插入图片描述
11. 定义并初始化结构变量structA,包含3个字段,把structA中的数据以JSON格式输出,要求:字段2不输出,字段3输出时重定义字段名。

type Movie struct {
	Title  string
	Actors []string `json:"-"`
	Year   int      `json:"released"`
}

func exercise11() {
	movie := Movie{"Bullitt", []string{"Steve McQueen", "Jacqueline Bisset"}, 1968}
	data, err := json.Marshal(movie)
	if err != nil {
		fmt.Printf("JSON marshaling failed: %s\n", err)
	}
	fmt.Printf("%s\n", data)
}

//忽略空值 "omitempty" 忽略字段 "-"

在这里插入图片描述
12. 编写函数expand,将s中的"foo"替换为f(“foo”)的返回值。
func expand(s string, f func(string) string) string

//函数值,在Go中,函数被看作第一类值(first-class values):函数像其他值一样,拥有类型,可以被赋值给其他变量,传递给函数,从函数返回。
func exercise12() {
	s := "fooballfoolbalfood"
	s = expand(s, expand2)
	fmt.Printf("%T\n", expand2) //"func(string) string"
	fmt.Println(s)
}

func expand(s string, f func(string) string) string {
	str := f("foo")
	s = strings.Replace(s, "foo", str, -1)
	return s
}
func expand2(s string) string {
	return s + "t"
}

在这里插入图片描述

  1. 方法和函数有什么区别?调用方法时有哪几种传递接收者的方式,有何区别?

//exercise13
//在函数声明时,在其名字之前放上一个变量,即是一个方法。这个附加的参数会将该函数附加到这种类型上,即相当于为这种类型定义了一个独占的方法。
//调用方法时有两种传递接收者的方式,传递值和传递指针。它们的区别如下:
//1、值传递对每一个参数值进行拷贝,传递指针可以对原变量进行更新。当这个接受者变量本身比较大时,我们就可以用其指针而不是对象来声明方法。
//2、传递指针的方法写法不同,类型前加上一个*,比如(p Point)。调用这个方法时,接收器是个指针,变量前加上&表示取该变量的地址,如 r := &Point{1, 2},
//如果接收器实参是类型T,但接收器形参是类型
T,这种情况下编译器会隐式地为我们取变量的地址。

  1. 用代码实现以下需求:
    (1)定义BaseData接口,包含add方法,该方法接收一个int参数,方法含义为在原有数据基础上增加新数值。
    (2)定义MyData接口,在BaseData接口基础上扩充一个toString方法,返回字符串,方法含义为返回内部数据的string形式。
    (3)定义MyIntData类型,实现MyData接口。内部包含一个int类型数据,add方法的实现逻辑为:更新内部的int类型数据,在原数据基础上加上传入的int参数值。
    (4)定义MyStringData类型,实现MyData接口。内部包含一个string类型数据,add方法的实现逻辑为:更新内部的string类型数据,把传入的int参数值作为字符串拼接在原有数据的后面。
    (5)实现函数addAndPrint(myData MyData, n int),对myData执行add(n)操作,并打印myData的toString结果。
    (6)分别构造MyIntData和MyStringData类型值,调用addAndPrint函数,检查结果是否符合预期。
//1.定义BaseData接口
type BaseData interface {
	add(addition int)
}

//2.定义MyData接口
type MyData interface {
	BaseData
	tostring() string
}

//3.定义MyIntData类型,实现MyData接口所有方法,basenum为内含int数据
type MyIntData struct {
	basenum int
}

func (m *MyIntData) add(addition int) {
	m.basenum += addition
}

func (m *MyIntData) tostring() string {
	return strconv.Itoa(m.basenum)
}

//4.定义MyStringData类型,实现MyData接口所有方法,basestr为内含string数据
type MyStringData struct {
	basestr string
}

func (m *MyStringData) add(addition int) {
	m.basestr += strconv.Itoa(addition)
}

func (m *MyStringData) tostring() string {
	return m.basestr
}

//5.实现函数addAndPrint(myData MyData, n int),对myData执行add(n)操作,并打印myData的toString结果。
func addAndPrint(myData MyData, n int) {
	myData.add(n)
	fmt.Println(myData.tostring())
}

//6.分别构造MyIntData和MyStringData类型值,编写测试函数,调用addAndPrint函数,结果符合预期。
func exercise14() {
	var myIntData MyIntData = MyIntData{basenum: 10}
	addAndPrint(&myIntData, 1)
	var myStringData = MyStringData{basestr: "myStringData"}
	addAndPrint(&myStringData, 1)
}

//打印:
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wsws100

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值