首页 网维知识库 Go语言并不简单

Go语言并不简单

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

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

Go语言并不简单插图

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

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

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

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

n := 0
for _, 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/op
NewArrayPreAlloc    525ns/op    896 B/op   1 allocs/op
NewArray           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 语言,但并不能帮助你真正学习这门语言。

免责声明:文章内容不代表本站立场,本站不对其内容的真实性、完整性、准确性给予任何担保、暗示和承诺,仅供读者参考,文章版权归原作者所有。如本文内容影响到您的合法权益(内容、图片等),请及时联系本站,我们会及时删除处理。

作者: 3182235786a

为您推荐

windows8

windows8

Windows 8 是微软公司于 2012 年推出的一款操作系统,因其独特的界面设计和功能受到广泛关注。本文将从 Win...
Windows 下载指南:获取最新版本的 Windows 操作系统

Windows 下载指南:获取最新版本的 Windows 操作系统

作为全球最受欢迎的操作系统之一,Windows 提供了丰富的功能和用户友好的界面。如果您想获取最新版本的 Windows...
windows资源管理器已停止工作

windows资源管理器已停止工作

Windows 资源管理器已停止工作是 Windows 操作系统中常见的一个问题,通常表现为资源管理器窗口无法正常打开或...
Windows 10 激活方法详解:轻松激活您的操作系统

Windows 10 激活方法详解:轻松激活您的操作系统

购买了全新的Windows 10操作系统后,如何激活它成为许多用户关注的问题。本文将为您详细介绍Windows 10的激...
windows10激活工具

windows10激活工具

Windows 10 激活工具是一款用于激活 Windows 10 操作系统的软件。通过使用激活工具,用户可以轻松地激活...

发表回复

返回顶部