查看原文
其他

Go语言不简单

Martin Tournoij CSDN 2021-04-25

【CSDN 编者按】相信你跟我一样,在以往听到的言论中都认为 Go 语言很简单,很容易学,但是事实真的是这样的吗?反方队伍认为 Go 语言并不简单,下面请反方辩手发言~


作者 | Martin Tournoij
译者 | 弯月
出品 | CSDN(ID:CSDNnews)

Go 不是一种很简单的编程语言。尽管它的许多方面都很简单:语法很简单,大多数语义也很简单。然而,语言不仅仅是语法,我们希望利用它编写出实用的代码。利用 Go 编写有用的代码并不总是那么容易。

事实证明,通过某种方式将一些简单的功能组合在一起,编写出有用的代码可能会非常棘手。在 Ruby 中,如何删除某个数组中的一项?list.delete_at(i)。如何通过值删除条目?list.delete(value)。非常简单!

然而在 Go 中,事情可没有那么容易,为了删除索引 i,你需要执行以下操作:

list = append(list[:i], list[i+1:]...)

为了删除值 v,你必须使用循环:

n := 0for _, l := range list { if l != v { list[n] = l n++ }}list = list[:n]

这未免也太复杂了?也未必,我认为即使没有 Go 语言经验,大多数程序员也可以看懂上述代码。但它确实不简单。我这个人比较懒,我会从 SliceTricks 上复制这类代码,因为我想专心解决实际问题,不想为这类小事苦恼。

此外,Go 语言也很容易出现使用错误或性能不佳的情况,特别是对于经验不足的程序员而言。例如,我们来比较一下:将上述复制到一个新数组,和复制到一个新的预分配数组(make([]string, 0,len(list))):

InPlace 116ns/op 0 B/op 0 allocs/opNewArrayPreAlloc 525ns/op 896 B/op 1 allocs/opNewArray 1529ns/op 2040 B/op 8 allocs/op

尽管在大多数情况下 1529ns 足够快了,而且也不必过分担心,但是在许多情况下,性能确实很重要,而且拥有能保证实现最佳性能的 list.delete(value)是非常有必要的。



再举一个例子:goroutine。“使用 goroutine 并不难,你只需要添加关键字 go,就可以了!”没错,这样确实可以了,但是如果我需要同时运行 500 万个 goroutine 呢?到时候,你会纳闷,所有内存都去哪儿了?而且你很难避免意外“泄漏”goroutine。

有许多模式可以限制 goroutine 的数量,但哪一种都不简单。下面就是一个简单的例子:

var ( jobs = 20 // Run 20 jobs in total. running = make(chanbool, 3) // Limit concurrent jobs to 3. done = make(chan bool) // Signal that all jobs are done.)
for i := 1; i <= jobs; i++ { running <- true //Fill running; this will block and wait if it's already full.
// Start a job. go func(i int) { defer func() { <-running // Drain runningso new jobs can be added. if i == jobs {// Last job, signal that we're done. done <-true } }()
// "dowork" time.Sleep(1 *time.Second) fmt.Println(i) }(i)}
<-done // Wait until all jobs are done.fmt.Println("done")

我加了一些注释是有原因的:对于不熟悉 Go 的人来说,这段代码非常难以理解。上述代码也不能确保数字会按照一定的顺序输出(这可能是一项需求,当然也可能不是)。

Go 的并发原语很简单且易于使用,但是将它们组合起来,解决常见的现实问题就没有那么简单了。



RichHickey 在“Simple Made Easy”中提出,我们不应该将“简单”与“易于编写”混为一谈:即便你只需编写一两行代码,也并不意味着底层的概念很简单(这里的简单指的是浅显易懂)。

这句话值得人寻味。在大多数情况下,我们不应该为了“易于编写”而牺牲“简单”。但这并不意味着我们不应该考虑如何让编程更加简单。即便概念很简单,也并不意味着易于使用,人们可能会错误地使用,或使用的方式会引发 bug。将 Hickey 的论点推到极致,就会出现 Brainfuck 之类语言,当然这很愚蠢。

理想情况下,编程语言应该减少推理其行为所需的认知负担,增加这种认知负担的方法有很多:复杂的语言功能就是其中之一;而人们不得不花费精力实现一些简单的概念也是一种负担,因为我需要多考虑一段代码。尽管我不太关心代码格式或语法选择,但我认为减少阅读代码时的认知负担很重要。

缺少泛型是导致 Go 不那么简单的部分原因。现在很难实现 slices 包之类以通用的方式完成的操作。而泛型可以让这成为可能,同时也会让编程变得更复杂(使用了更多的语言功能),但是它们也可以让编程更加容易,并降低其他方面的复杂性。



这些是无法克服的问题吗?不,我仍然会使用 Go,而且也会一如既往地喜欢 Go。但是,我不认为 Go 是你“可以在 5~10 分钟之内学会的语言”。

归根结底,学习语言不仅仅要学习编写 if 和 for 的语法,你需要学习的是思维方式。我见过许多 Python 或 C#开发人员尝试在 Go 语言中实现那些语言的某些概念或模式。常见的做法包括将结构嵌入作为继承,将 panics 作为异常,通过 interface{}实现“伪动态编程”等等。这些做法很难取得良好的结果。

当第一次编写 Go 程序时,我也犯了同样的错误,这是很自然的事情。在刚接触 Ruby 的时候,我曾尝试用 Ruby 编写 Python 代码(由于这两种语言很相似,所以结果相对好一点,但仍然有很多奇怪的做法,比如使用 for 循环)。

这就是为什么我不喜欢人们通过 Go 教程学习这门语言的原因,教程只会讲解基本的语法,还有其他的一些知识。这只能让你大致感受一下 Go 语言,但并不能帮助你真正学习这门语言。

英文标题:Go is not an easy language

原文链接:https://www.arp242.net/go-easy.html

声明:本文为 CSDN 翻译,转载请注明出处。

49 岁的红杉资本遭遇黑客攻击

碾压专业机构,27岁华裔小伙推出美国最准新冠预测模型

iOS 14.5 更新,苹果手表可以解锁 iPhone 了!

尴尬!因软件 Bug ,美国数百名囚犯释放后无法出狱

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存