Go语言在解决实际问题时的优点与不便

前言

Go语言,全称golang,是Google开发的一种静态强类型、编译型、并发型并具有垃圾回收功能的编程语言。 从2007年末由 Robert Griesemer、Rob Pike、Ken Thompson 主持开发,其中的 Ken Thompson 可是和 Dennis Ritchie 一起发明了C语言的大佬。Go 语言于2009年11月正式宣布成为开放源代码项目, 并在2012年初,发布了Go 1.0稳定版本,此后便开启了稳步发展的道路。

Go 语言

作为一个从2007年开始诞生的语言,在庞大的语言家族中算是一个晚辈,和C++、Python这种老牌语言相比查了将近20年,和 C 语言相比资历就更低了,但是这个新晋的语言在 Google 光环的强大加持下也在飞速发展着,由于前辈们在发展的途中趟了很多坑,所以 Go 在发明之初就避免了其他语言的很多不便,可以说是站在巨人的肩膀上发展起来的。

但是即便这样,Go 语言的特点也不能被所有人喜欢,和许多人一样,我在学习这门语言的过程中也发现一些很方便特性和一些不太方便的特点,下面简单说几个点,有不对的地方希望小伙伴能及时指出,防止我在错误的思想上越走越远(怎么有种新闻发言稿的感觉~)。

不便之处

这里的不便之处只是我在使用过程中感觉不太方便,可能很多人并没有这个感觉,或许还有很多其他的解决方法和替代方案,烦请小伙伴能指点一下。

三目运算符

这个三目运算符是个很常用的逻辑处理部件,也是我在逻辑中经常使用到的,在Python、Lua等语言中也不存在,但是我都找到了简单的替代方式,但是在Go 中不得不写成中规中矩的 if 条件判断,这让很多算法的解题代码看起来并不那么优雅,比如一个简单的约瑟夫环问题:

N个人围成一圈,从第一个人从开始报数,报到m的人出圈,剩下的人继续从1开始报数,报到m的人出圈;如此往复,直到所有人出圈,输出最后一个出圈人最初始的编号。

这个问题的解法最简单的是模拟法,使用数组模拟一个环来按照规则运行,最后一个出圈的人的编号就可以输出到结果,还有一种思路就是找规律,可以找到出圈前后的序号对应关系,进而写出一行代码的解决方案。

1
2
3
4
// 索引从0开始,只要对结果加1就好了
int joseph_ring(int n, int m) {
return n == 1 ? 0 : (joseph_ring(n - 1, m) + m) % n
}

但是在没有三目运算符的 Go 中要实现这个算法,就不得不多写几行了,和 C 语言相比就没有那么简洁了。

1
2
3
4
5
6
7
func joseph_ring(n int, m int) int {
if n == 1 {
return 0
}

return (joseph_ring(n - 1, m) + m) % n
}

if 单行语句也要加大括号

Go 语言本身带有自己的格式化命令,可以保证编写时不同的缩进样式格式化之后得到相同的代码,if 后面的条件语句可以不加小括号,但是后面的语句块必须加大括号,这样的规定对于我经常写的代码有点不太友好,比如下面这些C++代码:

1
2
3
4
5
6
7
8
if (a > 0)
break;

if (b < 0)
continue;

if (c == 0)
return;

有时候为了看起来紧凑,可能会写成这样:

1
2
3
if (a > 0) break;
if (b < 0) continue;
if (c == 0) return;

但是放到 Go 语言中,就不得不写成好几行了,并且还要加大括号,看起来代码有些松散。

1
2
3
4
5
6
7
8
9
10
11
if a > 0 {
break;
}

if b < 0 {
continue;
}

if c == 0 {
return;
}

优秀特性

上面提到了 Go 语言中不方便的地方,现在可以来说说 Go 语言相对于 C、C++ 更优越的特性:

多个变量同时赋值

在 C++ 中交换两个变量的通常使用中间变量来完成,比如交换 ab 两个变量的值:

1
2
3
4
5
6
int a = 1;
int b = 6;

int tmp = a;
a = b;
b = tmp;

针对于这种整形的变量,一些大牛们发明了特殊的算法来处理,避免使用中间变量:

1
2
3
4
5
6
int a = 1,;
int b = 6;

a = a ^ b
b = b ^ a
a = a ^ b

但是在 Go语言中这种情况非常好处理,直接从左到右依次赋值就好了

1
2
3
4
var a int = 1
var b int = 6

b, a = a, b

defer 声明

defer 可以用于在当前函数返回前执行一些清理代码,而不管此函数如何退出。defer 在函数中可以随时出现,这使得清理代码可以尽可能在需要清理的地方运行,比如我们常常要释放申请的资源,常见的需要释放的资源有文件描述符:

1
2
3
4
5
file, err := os.Open(fileName)
if err != nil {
return
}
defer file.Close()

有了 defer 终于不再在担心,资源没回收的问题,也不用在各个提前返回的条件分支中添加释放资源的重复代码了。

goroutine 并发

goroutine 是Go并行设计的核心,说到底其实就是协程,但是它比线程更小并且在Go语言内部帮你实现了这些 goroutine 之间的内存共享。执行 goroutine 只需要极少的栈内存,可同时运行成千上万个并发任务。goroutine 一定程度上比 thread 更易用、更高效、更轻便。

使用起来也非常方便,创建 goroutine 只需在函数调用语句前添加 go 关键字,就可以创建并发执行单元。开发人员无需了解任何执行细节,调度器会自动将其安排到合适的系统线程上执行,这是解放生产力的又一创举,简单示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package main

import (
"fmt"
"time"
)

func new_task() {
for i := 0; i < 10; i++ {
fmt.Println("this is a newTask")
time.Sleep(time.Second) //延时1s
}
}

func main() {
for i := 0; i < 3; i++ {
go new_task() //新建一个协程, 新建一个任务
}

time.Sleep(time.Second * 15) //延时15s
fmt.Println("this is a main goroutine")
}

总结

  • Go 语言作为编程语言中的新晋小弟,吸收了前人的经验,现阶段发展迅猛
  • 虽然 Go 出于一些目的规定了语言的标准,但是类似于没有三目运算符这种特点还是有些不方便
  • Go 这门语言还很年轻,相信随着不断发展它会越来越优秀,但没有任何语言是完美无缺的

==>> 反爬链接,请勿点击,原地爆炸,概不负责!<<==

陪伴是最长情的告白,而守护是最沉默的陪伴。国庆中秋双节合并,放假了,陪家人待在一起真的很开心,什么都不用做,就静静的待在一起很满足,聊聊天、抬抬杠,假期嗖嗖嗖地溜掉了~

Albert Shi wechat
欢迎您扫一扫上面的微信公众号,订阅我的博客