go语言学习遇到的问题

go语言学习遇到的问题

最近在努力学习go,这个编程语言语法和写法的都和其他的编程语言有着比较大的差异。所以特地记录一下学习go遇到的问题。

GOPATH和go module

这个问题比较神奇,也是我学习这个编程语言中遇到的比较拧巴的问题。网上的绝大多数的教程都会让你配置GOPATH,然后go程序都必须在GOPATH的SRC这个文件下运行,而这个GOPATH是你windows的环境变量。

在以前GOPATH下会对go程序的编写严格进行,必须在src下,并且在很多包,或者git下其他的程序之后,整个项目就会变得非常混乱

这样听起来很奇怪不是吗?身为一个比较新鲜的编程语言,既然会有这么愚蠢的方法。

而网上的教程绝大多数度是让你去配置环境,最后用vscode去编写go程序,但是我自己在用IDEA去写的时候却不会遇到要配置GOPATH这个问题,这是为什么呢?然后我去网上进行了一番搜索之后,了解了一个新鲜的事物,叫做go module。

而go 1.13版本之后就支持了go module,这使得程序的编写不用在指定的文件夹进行,而是用模块化的方式去编写程序。

而现在,在有了MODULE之后,GOPATH现在常用于存放第三方API,不进行程序编写了。go module允许go在任意地方进行编写,非常人性化。

1
2
3
4
5
6
7
当你在GOPATH下使用go module时
用环境变量 GO111MODULE 开启或关闭模块支持,它有三个可选值:off、on、auto,默认值是 auto。

GO111MODULE=off 无模块支持,go 会从 GOPATH 和 vendor 文件夹寻找包。
GO111MODULE=on 模块支持,go 会忽略 GOPATH 和 vendor 文件夹,只根据 go.mod 下载依赖。
GO111MODULE=auto 在 $GOPATH/src 外面且根目录有 go.mod 文件时,开启模块支持。
但是我建议新开别的文件夹去写go程序,不要在GoPATH下

所以,我们应该使用 go mod init your_project_name

去实现go的模块化编写

浅拷贝和深拷贝的问题

在我用go写leetcode39的时候,遇到了一个奇怪的问题,我加入ans的值是【2,2,2,3】,可是运行结果后,值就变为【2,2,2,1】了,真是奇怪。我在func中有且仅有一个对ans的操作,这真的让我思考了很久,然后一个一个的debug。。。。。。。。

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
func combinationSum(candidates []int, target int) [][]int {
ans = nil
var currentArray []int
//不断递归搜索的问题
for i := 0; i < len(candidates); i++ {
currentArray = nil
currentArray = append(currentArray, candidates[i])
//如果当前就是,那么久不用继续了
if candidates[i] == target {
singerAns := []int{candidates[i]}
ans = append(ans, singerAns)
} else {

//否则递归性搜索
combinationSumRecursiveFunction(candidates, currentArray, i, candidates[i], target)
}
}

//for i := 0; i < len(ans); i++ {
// sum := 0
// for j := 0; j < len(ans[i]); j++ {
// sum += ans[i][j]
// }
// if sum != target {
// fmt.Println(ans[i])
// }
//}

return ans
}
func combinationSumRecursiveFunction(candidates, currentArray []int, begin, sum, target int) {
for i := begin; i < len(candidates); i++ {
if candidates[i]+sum == target {
//不要去改动currentArray
//等于就加入ans
correctArr := currentArray
correctArr = append(correctArr, candidates[i])
ans = append(ans, correctArr)
} else if candidates[i]+sum < target {
//小于则可以直接递归,再次搜索
smallerArr := currentArray
smallerArr = append(smallerArr, candidates[i])
combinationSumRecursiveFunction(candidates, smallerArr, i, candidates[i]+sum, target)
} else {
//大于则没有继续搜索的必要了,什么都不做就可以了
}
}

}

var ans [][]int

最后,我花了一个小时,终于找到了问题所在,问题在于这个:

1
2
3
4
5
correctArr := currentArray
correctArr = append(correctArr, candidates[i])
//========================================================================================
smallerArr := currentArray
smallerArr = append(smallerArr, candidates[i])

它们之间是浅拷贝关系,我对smallerArr 进行append也会影响到已经加入了ans的数组!!!!!

  1. 调用append函数时,当原有长度加上新追加的长度如果超过容量则会新建一个数组,新旧切片会指向不同的数组;
  2. 如果没有超过容量则在原有数组上追加元素,新旧切片会指向相同的数组,这时对其中一个切片的修改会同时影响到另一个切片。

改动了正确代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
func combinationSum(candidates []int, target int) [][]int {
ans = nil
var currentArray []int
//不断递归搜索的问题
for i := 0; i < len(candidates); i++ {
currentArray = nil
currentArray = append(currentArray, candidates[i])
//如果当前就是,那么久不用继续了
if candidates[i] == target {
singerAns := []int{candidates[i]}
ans = append(ans, singerAns)
} else {

//否则递归性搜索
combinationSumRecursiveFunction(candidates, currentArray, i, candidates[i], target)
}
}

//for i := 0; i < len(ans); i++ {
// sum := 0
// for j := 0; j < len(ans[i]); j++ {
// sum += ans[i][j]
// }
// if sum != target {
// fmt.Println(ans[i])
// }
//}

return ans
}
func combinationSumRecursiveFunction(candidates, currentArray []int, begin, sum, target int) {
for i := begin; i < len(candidates); i++ {
if candidates[i]+sum == target {
//不要去改动currentArray
//等于就加入ans
//深拷贝
correctArr := make([]int, len(currentArray))
copy(correctArr, currentArray)
//浅拷贝
//correctArr := currentArray
correctArr = append(correctArr, candidates[i])
ans = append(ans, correctArr)
} else if candidates[i]+sum < target {
//小于则可以直接递归,再次搜索
smallerArr := currentArray
smallerArr = append(smallerArr, candidates[i])
combinationSumRecursiveFunction(candidates, smallerArr, i, candidates[i]+sum, target)
} else {
//大于则没有继续搜索的必要了,什么都不做就可以了
}
}

}

var ans [][]int

未完待续。。。。。

1、深拷贝(Deep Copy):

拷贝的是数据本身,创造一个样的新对象,新创建的对象与原对象不共享内存,新创建的对象在内存中开辟一个新的内存地址,新对象值修改时不会影响原对象值。既然内存地址不同,释放内存地址时,可分别释放。

值类型的数据,默认全部都是深复制,Array、Int、String、Struct、Float,Bool。

2、浅拷贝(Shallow Copy):

拷贝的是数据地址,只复制指向的对象的指针,此时新对象和老对象指向的内存地址是一样的,新对象值修改时老对象也会变化。释放内存地址时,同时释放内存地址。

引用类型的数据,默认全部都是浅复制,Slice,Map。