我最近对区块链开源社区产生了兴趣。 我注意到的第一件事是,我遇到的大多数项目都是用Go编写的。 围棋很久以来一直是我的关注焦点,因为社区对此感到非常兴奋并对此表示高度赞扬。
为什么去?
Go是一种静态类型的编译语言,例如C ++和Java。 它遵循语法和语义上“ C”的简单性,并具有一些附加功能。
例行程序
与较早的编程语言(Python,Java等)相比,Go是在2009年发布的。Go的构建考虑了并发性以及能够有效利用多处理器的能力。 Rob Pike(Go创作者)对此进行了深入的讨论。 最终结果是运行并发go例程比使用Java等其他语言的线程要便宜得多。 这使我们可以同时运行数千个goroutine (甚至在单个处理器上!),而无需担心,并且使其成为需要扩展的问题的理想解决方案。
代码执行
Go直接在底层硬件上运行。 Go不在VM上执行,这意味着我们的代码被直接编译为二进制文件以在处理器上运行。 消除中间人VM(例如Java情况下的JVM)意味着perf处于尽可能低的水平,并且我们能够通过消除不必要的抽象来微调和优化代码,以最大程度地发挥作用。和性能命中。
职能是头等公民
在Go中,将函数视为其他任何数据类型。 这意味着函数可以返回函数,接受函数等。 这使我们能够利用函数式编程范例(我喜欢!),并编写更清晰,更清晰的代码。
可维护性
Go语法简洁明了。 此外,它还有一个出色的格式化程序,可以用一种方式格式化代码。 这听起来似乎很痛苦,但实际上解决了许多围绕编码约定的麻烦。Go消除了许多语言功能(IMO),这些功能使代码更难以理解,例如继承,构造函数,注释,泛型等等。
包装
Go允许将相关代码分组。 编译时,程序包共享一个名称空间。 因此,您可以在单独的文件中编写代码,但是仍然可以调用位于不同目录或文件中的代码。 随着项目的复杂性增加,您可以创建许多软件包,每个软件包代表应用程序的不同逻辑单元。 但是,对于软件包,我们使用了一个特殊的名称来告诉Go我们希望将其转换为可以执行的文件。 该名称是“ main” 。
![](https://i-blog.csdnimg.cn/blog_migrate/9f5977589baba342dacd00dd50a75126.png)
因此,将包命名为“ main”将告诉Go我们希望它可执行。 一旦Go知道该文件是可执行文件,它将需要我们定义一个名为“ main”的函数。 程序运行时,将自动调用此main
功能。
Go有一个非常强大的标准库。 为了访问,我们只需在包声明下方添加import语句。 入门应该看起来像这样:
package main
import (
"fmt"
)
func main() {
fmt.Println("Hello world") // Amazing logic here
}
变数
在Go中使用变量很熟悉,但又有所不同。 让我们研究一下变量赋值,稍后我们将研究按值与按引用使用变量。 这是在Go中初始化变量的有效方法。
var currentDay string = “Saturday”
在一个示例中,它看起来像这样:
![](https://i-blog.csdnimg.cn/blog_migrate/5046922e94e87203c42d395f3c396b19.png)
但这很冗长。 Go允许将变量声明和赋值简化为:
currentDay := “Saturday”
在这里可以推断出“ currentDay”是一个字符串,因此无需对其进行明确说明。 同样,Go知道以下是一个“ Integer”声明。
luckyNumber := 3
因此,以下两种是声明和初始化Go变量的等效方法。
luckyNumber := 3
var luckyNumber int = 3 // oops! This will cause an error!
不过要小心! 在Go中,我们只能将变量初始化一次。 因此,以上代码片段将无法编译!
![](https://i-blog.csdnimg.cn/blog_migrate/c6ad8fd1011756e27c2e2b4a56f5ab42.png)
功能
Go是一种静态类型的编程语言,因此我们需要为每个函数注释返回值的类型。 以下代码将不会编译,因为我们尚未注释返回类型:
func greetUser() { // function declaration is missing return type!
return “Hey friend!”
}
要解决此错误,我们只需在函数声明中添加返回类型。
![](https://i-blog.csdnimg.cn/blog_migrate/e72aaf6892b678ef734d5b41721c5dd8.png)
从main方法内部调用此函数如下所示:
![](https://i-blog.csdnimg.cn/blog_migrate/3710c52e9379c809ded0ca1cda1757db.png)
凉。 让我们来看一个示例,其中将到目前为止所看到的一切都捆绑在一起。 让我们在不同的文件中声明greetUser,但是使用相同的包并调用它。 回想一下,同一包中的文件共享一个名称空间,并且可以自由地相互调用。
![](https://i-blog.csdnimg.cn/blog_migrate/6136a7525e58e266d6d67d72b88339dc.png)
我们用
go run utils.go main.go
我们得到以下内容:
![](https://i-blog.csdnimg.cn/blog_migrate/e1635220e40a0ec29e879f6d32ece69f.png)
切片和for循环
在Go中,数组是静态的,创建时需要指定其长度。 如果要使用动态大小的数组,则需要使用slice 。 声明切片很简单:
fruits := []string{“Mango”, “Cherry”, “Apple”}
请注意,切片中只能包含一种类型的值。 因此,用一个int以及一个字符串声明一个切片将无法编译。
现在让我们对水果切片进行基本迭代,并记录每个元素。
![](https://i-blog.csdnimg.cn/blog_migrate/3269c07ff15027c87f558228ad779364.png)
Don’t forget to build with go run main.go
![](https://i-blog.csdnimg.cn/blog_migrate/998c4605f120b1e0a02741e18566b6a6.png)
一个陷阱是Go要求我们在代码中使用所有声明的变量。 如果我们不希望该索引,则可以通过将其命名为“下划线”来告诉Go忽略它,如下所示:
![](https://i-blog.csdnimg.cn/blog_migrate/d80aed2df0310e80023db7ac15f18789.png)
![](https://i-blog.csdnimg.cn/blog_migrate/7a3bde7a86055349691522c19a4dddba.png)
切片如何在引擎盖下工作
Go如何处理创建这些动态切片? Go的方法很有趣。 创建切片时,将在后台创建两个数据结构 。 第一个是数组,第二个是记录切片的长度,切片的容量以及对该数组的引用的结构。
是一种面向对象的语言吗?
根据文档“是”和“否”(是的,答案很明确!)。 Go采用了不同的方法来尝试获得相似的结果。 在Go中,没有“类”。 相反,Go允许我们为特定数据类型声明“接收器函数”。 理解这一点的最简单方法是看一些代码。
![](https://i-blog.csdnimg.cn/blog_migrate/f9eb19daeed09ad84f9e7c96290ae3b0.png)
因此,尽管我们无法创建Day类(就像现在使用经典的OOP语言一样-如今考虑使用Java,Python甚至是JS),我们仍然可以轻松地声明这些类型的新数据类型和方法。
因此,让我们桥接Go术语并将其与经典OOP中的内容进行比较。
通过使用带有接收器的函数创建新类型,我们可以为该类型的任何值添加“方法”。 (斯蒂芬·格勒)
多个返回值
我在Go中真正喜欢的功能是能够从一个函数返回多个值。 让我们在实践中看一下:
![](https://i-blog.csdnimg.cn/blog_migrate/606f1f12acc18b85b56eb60222dbf999.png)
结构
Go的结构是可变字段的类型化集合 。 如果您来自JS,python或Java,那么它实际上是一个对象。 它们对于将数据分组在一起以形成记录很有用。
![](https://i-blog.csdnimg.cn/blog_migrate/89ed7782137d29bb93f22181af224c85.png)
![](https://i-blog.csdnimg.cn/blog_migrate/168e3b256f90afd0595b3d9ac4e7f7f5.png)
地址和指针
Go区分两种不同的数据类型。
值类型 -包含原始数据类型的实际值的类型。 考虑一下int,float,string等。
引用类型 -包含对记录的实际基础列表的引用的任何数据类型。
这是对话开始变得更加有趣的地方。 默认情况下, 在将参数类型(例如int,float,string或struct)传递给函数时,Go复制每个参数并将其按值传递。 有了这些知识,以下示例将按价值传递。 您希望它打印什么?
![](https://i-blog.csdnimg.cn/blog_migrate/9d6d51a305f9ea50d1f43b54a7eb6070.png)
因为Go复制了“值类型”,并且没有向它们发送以上输出的引用:
![](https://i-blog.csdnimg.cn/blog_migrate/8896118b3554f05798fef1847543b286.png)
我们刚刚看到Go中的变量是按值传递的。 就像我们前面提到的那样,当创建切片时,将创建一个在切片上具有元数据的附加数据结构。 这包括对slice的引用。 每当我们谈论切片时,都会使用并引用该引用。 因此,以下示例将成功使切片的内容发生变异。
![](https://i-blog.csdnimg.cn/blog_migrate/b396f1d05f94a400a669f3fec454891a.png)
![](https://i-blog.csdnimg.cn/blog_migrate/0edb97ec904fcae915a15fd6e5066afd.png)
但是,如果我们想对发送给函数的结构数据进行突变会怎样? 我们需要通过引用。 Go在这个概念上有点低级,要求我们传递变量的地址和取消引用指针。
除了:指针只是计算机内存中变量的地址。 如果我们发送变量的地址而不是值,则可以直接对其进行操作。
获取变量的地址
我们始终可以使用&运算符来获取变量的地址。
![](https://i-blog.csdnimg.cn/blog_migrate/123bd4b66727b971c209791de3618d1e.png)
这将输出物理内存中变量的实际地址 。
![](https://i-blog.csdnimg.cn/blog_migrate/358fa6dd9dd32d4e312ea36c5571aea6.png)
现在,我们已经有了对变量(地址)的引用 ,我们可以通过取消引用指针并分配新的内容来轻松更改其值。
另外: 取消引用是从地址中提取值的动作。 我们用*运算符取消引用地址。
因此,如果我们希望我们的函数接收一个指向日类型的指针(上一个示例中的结构) ,然后记录其值,我们将尝试执行以下操作:
![](https://i-blog.csdnimg.cn/blog_migrate/acecfd9f7cde030909e1e936397c420d.png)
让我们看一个使用接收器函数时按引用传递的示例。
![](https://i-blog.csdnimg.cn/blog_migrate/6c433c5d572c2a0c902de65c72ffccac.png)
那么为什么现在可以工作呢? 我们更改了updateDaySuccess接收器函数,以接受指向day类型的指针。
重要:调用方法时,Go识别出它正在期望一个指针,并为我们隐式发送对该值的引用(谢谢Go!)。然后,updateDaySuccess接收到该指针,并使用*运算符对其取消引用,并分配一个新值。
地图
哈希表是最有用的数据结构之一。 它们提供快速查找,添加和删除。 Go提供了实现哈希表的内置映射类型。 在Go中,键和值可以是带有空接口的几种不同类型。 在该“转到游乐场”链接中,声明了具有几种不同类型的键的地图。 除此之外,它们的工作原理与您对它们的期望也非常相似。
![](https://i-blog.csdnimg.cn/blog_migrate/fce8c8c756d38d1868a565e250d3d4a4.png)
![](https://i-blog.csdnimg.cn/blog_migrate/dc561355dbd6c7b144a2f3d1f3f16805.png)
介面
Go中的接口使代码更加灵活。 接口无需要求特定的类型,而是允许我们指定需要某些行为。 在Go中,接口是隐式满足的。 这意味着我们无需编写诸如blahBlah之类的东西 (我在看Java ppl) 。 当我们说类型满足接口时,是指该类型实现该接口定义中包含的所有功能。 让我们看一个基本的例子:
![](https://i-blog.csdnimg.cn/blog_migrate/a96f9304526c1c9a6835a1c74eb24f83.png)
因此,在这里我们可以说day结构体满足dayDetails接口,因为它定义了返回字符串的接收器函数getDay。 因此,如果您遇到了其他任何语言的界面,其原理和好处都将延续到Go中。
goroutine
goroutine是与其他功能或方法同时运行的功能(接收器或常规)。 goroutine本质上是一个线程,但是开销较小。 因为goroutine比线程轻,所以go程序通常要同时运行数千个例程。 启动goroutine很简单! 只需在go关键字前面加上,就会自动使go例程同时运行。 尽管启动围棋例程很简单,但是还是有一些陷阱。 让我们来看这个例子:
![](https://i-blog.csdnimg.cn/blog_migrate/aa9563e4e5dc7d5ec627b71a8ff4f120.png)
您期望输出是什么?
![](https://i-blog.csdnimg.cn/blog_migrate/0af7518031bb00e6190e151902880bd6.png)
为什么没有输出“星期六”?
创建新的go例程后,调用将立即返回。 与函数不同, 控件不等待goroutine完成执行。 因此,在这种情况下,程序流程将继续并结束,主程序将自行终止并拥有其所拥有的goroutine。 让我们通过休眠程序来克服这个问题。 这将使我们的goroutine有足够的时间来完成执行。
![](https://i-blog.csdnimg.cn/blog_migrate/d430fd383b9a8d76cc0bc572df2840e8.png)
![](https://i-blog.csdnimg.cn/blog_migrate/ff8abf86a4ccda6cb7216d5d6c8df25d.png)
这有效,但是超级hacky,而不是我们应该采用的做法。 渠道让我们做得更好。
频道
因此,我们已经看到很容易生成go例程。 通道是允许goroutine进行通信的工具。 通道允许我们将数据从一个goroutine传送到另一个goroutine(甚至使用pipe运算符!)。 每个通道都有与之关联的数据类型。 与通道关联的数据类型定义了我们可以通过其管道传输的数据。 例如,以下通道允许可以发送和接收布尔值:
boolChannel := make(chan bool)
通过通道发送和接收数据是阻止操作,因此我们可以轻松地重写上面的程序,而无需担心睡眠不足。
让我们看一下重写我们的程序!
![](https://i-blog.csdnimg.cn/blog_migrate/12404127d7107c08908bf483374d6d37.png)
![](https://i-blog.csdnimg.cn/blog_migrate/9d379cc4f68ad0c5961b0f91c513c6d6.png)
结论
这是Go功能的快速概述。 显然,这是冰山一角,还有很多发现:)我对Go的最初印象是它简洁而强大。 最近,我更喜欢使用静态类型的语言,因为它允许更快地开发,没有运行时错误,并且消除了整类的耗时错误! 我有兴趣学习Go来了解与区块链相关的项目,服务器以及任何需要并发的事物,因为它看起来既有趣又强大!
我的下一篇文章将探索区块链和Go,敬请期待!
如果此帖子有帮助,请订阅并单击下面的拍手ap按钮以显示您的支持! ⬇⬇
您可以在Instagram , Linkedin和Medium上关注我。
From: https://hackernoon.com/time-to-go-learning-golang-through-examples-480a90c5e7f9