财富之旅
A few words before we start. You can find the code used in this tutorial in this repository. You can find the full contents of Road to Go Pro here. If you missed the last one, you can find it via this link.
在我们开始之前的几句话。 您可以在此存储库中找到本教程中使用的代码。 您可以在此处找到Road to Go Pro的完整内容。 如果错过了最后一个,可以通过此链接找到它。
We talked about the basic types and data structures in the last part of the tutorial. In this one, we will talk about flow control, loops and thefmt
package.
在本教程的最后一部分中,我们讨论了基本类型和数据结构。 在这一节中,我们将讨论流控制,循环和fmt
包。
流量控制 (Flow Control)
如果(if)
Firstly, let’s look at the if
statement. One of the most basic flow controls is if and else statements. They control the program to execute different code blocks based on conditions. The syntax of if
statement looks like this.
首先,让我们看一下if
语句。 最基本的流程控制之一是if和else语句。 它们控制程序根据条件执行不同的代码块。 if
语句的语法如下所示。
When conditionA in the if statement returns true, then the code inside the if
brackets will execute. Otherwise, the conditions below will be evaluated in sequence until reaches the last else
code block. Go evaluates conditionB next and if conditionB returns true, the code inside will execute. If conditionB returns false, then the last code block will execute. In a combination of if
, else if
and else
statements, the order of evaluations is always from top to the bottom. Therefore, else
is used as a catch-all block in the end. Of course, you can omit the last else
block if you don’t need a catch-all function.
如果if语句中的conditionA返回true,则将执行if
括号内的代码。 否则,将依次评估以下条件,直到到达最后一个else
代码块。 Go接下来评估conditionB,如果conditionB返回true,则将执行其中的代码。 如果conditionB返回false,则将执行最后一个代码块。 在if
, else if
和else
语句的组合中,求值顺序始终是从上到下。 因此, else
最终将用作万能的块。 当然,如果不需要包罗万象的功能,则可以省略最后一个else
块。
So what does a condition look like? Any statements that return a boolean value can be placed in the condition. Here is an example. By the way, we have been using fmt.Println
for a while but I haven’t introduced it officially. In the last section of this tutorial, we will talk about the fmt
package.
那么条件是什么样的呢? 任何返回布尔值的语句都可以放在条件中。 这是一个例子。 顺便说一句,我们已经使用fmt.Println
时间了,但是我还没有正式介绍它。 在本教程的最后一部分,我们将讨论fmt
包。
You can also use logical operators in conditions. There are three logical operators, namely and (&&
), or (||
), not(!
). Conditions can be chained together via logical operators to form complex conditions.
您还可以在条件中使用逻辑运算符。 存在三个逻辑运算符,分别是和( &&
)或( ||
),不是( !
)。 条件可以通过逻辑运算符链接在一起以形成复杂的条件。
There is no limit on how many else if
statements you can write in a chain of if…else if … else
. Whereas for the benefit of readability, we do not recommend having a long list of if/else statements. switch
is the right choice to handle this scenario. Before we get to it, I want to introduce a special syntax of if
statements in Go to you.
您可以在if…else if … else
链中编写多少else if
语句没有其他限制。 出于可读性的考虑,我们不建议一长串if / else语句。 switch
是处理这种情况的正确选择。 在开始之前,我想在Go to you中引入if
语句的特殊语法。
Go can accept a “short statement” before the condition in if
/else if
statements. This syntax makes the code more concise. It is widely used for error handling, which we will cover in later tutorials. Let’s take a look at an example of this syntax.
Go可以在if
/ else if
语句中的条件之前接受“简短声明”。 此语法使代码更简洁。 它被广泛用于错误处理,我们将在以后的教程中介绍。 让我们看一下这种语法的一个例子。
A “short statement” is a declaration of one or more variables: they are short-lived and only accessible inside the closest brackets. In the example above, if we try to access the variable in the short statement outside the if
code block, we will get an error back. A common error handling pattern in Go uses short statements to check if there is an error returned by a function. Since the error returned is only related to the if
condition, we can use the “short statement” syntax to make code more concise and avoid confusions of variable scopes.
“简短声明”是对一个或多个变量的声明:它们是短期的,只能在最接近的括号内访问。 在上面的示例中,如果尝试在if
代码块外部的short语句中访问变量,则会返回错误。 Go中常见的错误处理模式使用短语句来检查函数是否返回错误。 由于返回的错误仅与if
条件有关,因此我们可以使用“短语句”语法使代码更简洁并避免变量范围的混淆。
Variable scope
可变范围
Let me quickly explain what the scope of a variable is. Not all variables are declared equally. Their accessibility depends on the place of declarations. For instance, if a variable is declared inside an if
block, we can only access it inside the same code block, not from outside. A general rule of thumb is: the reach of a variable is bounded with its closest brackets ({}
).
让我快速解释一下变量的范围是什么。 并非所有变量都被平等地声明。 它们的可访问性取决于声明的位置。 例如,如果在if
块内声明了变量,则我们只能在同一代码块内访问它,而不能从外部访问它。 一般的经验法则是:变量的范围以其最接近的方括号( {}
)为界。
In the example above, anotherPlace
and newPlace
are only accessible within the closest brackets around them. If there’s no bracket around a variable, then it is a global variable inside the package. We will discuss accessibility when we touch on packages. For now, remembering the bracket rule is sufficient.
在上面的示例中,只能在最近的方括号内访问anotherPlace
和newPlace
。 如果变量周围没有括号,则它是包内的全局变量。 当我们接触软件包时,我们将讨论可访问性。 现在,记住括号规则就足够了。
开关 (switch)
Alright, enough distractions. Let’s get back to the flow control. I mentioned that when we have a long list of conditions, it’s better to use switch
statements than if
statements. switch
is a cleaner form of if
, which looks like this.
好吧,足够的干扰。 让我们回到流控制。 我提到过,当条件列表很长时,使用switch
语句比使用if
语句更好。 switch
是if
的更干净形式,它看起来像这样。
The example switch statement can be transformed to the below if statement.
可以将示例switch语句转换为以下if语句。
The switch statement compares its associated variable with values in the case
statements inside. If it finds a match, the code inside that case
block will be executed. Once that block is finished, the program exits the switch block without checking other cases. The last default
block is like the else
statement.
switch语句将其关联变量与内部case
语句中的值进行比较。 如果找到匹配项,则将执行该case
块中的代码。 该块完成后,程序将退出切换块,而不检查其他情况。 最后一个default
块类似于else
语句。
You might not feel it is easier to write switch
statement if there are only 3 conditions to check. But imagine if you need to write 10 conditions like this. switch
will look much cleaner and concise than if
.
如果只检查3个条件,您可能会觉得编写switch
语句比较容易。 但是,假设您是否需要编写10个这样的条件。 switch
会看起来比更加干净,简洁if
。
switch
also has a more generic form where we don’t need to specify a variable after it. Instead, conditions can be specified in case
statements. For instance:
switch
还具有更通用的形式,我们不需要在其后指定变量。 而是可以在case
语句中指定条件。 例如:
Yes, case conditions in the generic switch statement can be any conditions that fit an if
statement (excluding the special short statement syntax).
是的,通用switch语句中的case条件可以是适合if
语句的任何条件(特殊的short语句语法除外)。
So when should we use if
and when should we use switch
? In my opinion, they’re interchangeable in many cases. Personally, I will choose whichever is cleaner visually and uses fewer lines of code.
那么什么时候应该使用if
以及什么时候应该使用switch
呢? 在我看来,它们在许多情况下都是可互换的。 就个人而言,我将选择视觉上更干净且使用更少代码行的那个。
循环 (Loop)
![Image for post](https://i-blog.csdnimg.cn/blog_migrate/fd4672b54d02df7913ac469d1159fcb9.png)
When we need to execute logic repeatedly, a loop is the best helper. Nobody wants to copy-paste duplicated code over and over again. In other languages, you’ve probably seen loops such as for
loops and while
loops. But in Go, there is only for
loop.
当我们需要重复执行逻辑时,循环是最好的帮助。 没有人愿意一遍又一遍地复制粘贴重复的代码。 在其他语言中,您可能已经看到了诸如for
循环和while
循环之类的循环。 但是在Go中,只有for
循环。
对于 (for)
For loop has four forms in Go.
For循环在Go中有四种形式。
Basic form
基本形式
The most basic form of for
loop looks exactly the same in many languages. For your information, I rarely use this form in real projects.
for
循环的最基本形式在许多语言中看起来完全相同。 仅供参考,我很少在实际项目中使用此表格。
Let’s quickly go through the basic form syntax.
让我们快速了解一下基本表单语法。
There are three short statements in the for loop above and they are separated by ;
s. The first short statement is for initiating the loop index. Usually, it starts with 0. The second short statement is the loop condition. As long as the loop condition is met, the loop keeps executing until the loop condition returns false
. And the last statement is the stepping function. We specify what’s going to change in each iteration. Most of the time, it’s going to be increasing/decreasing the loop index. The example above shows us how we can loop through a slice. We start with setting the loop index to 0. The loop should exit after reaching the last element in the slice. Thus, we set the condition to “loop index needs to be smaller than the length of the slice”. Lastly, we set the stepping function to “increase the index by 1”.
上面的for循环中有三个短语句,它们之间用;
分隔;
s。 第一条简短语句用于启动循环索引。 通常,它以0开头。第二个简短语句是循环条件。 只要满足循环条件,循环就会继续执行,直到循环条件返回false
为止。 最后一个语句是步进功能。 我们指定每次迭代中要更改的内容。 大多数时候,它会增加/减少循环索引。 上面的示例向我们展示了如何遍历切片。 我们首先将循环索引设置为0。循环应在到达切片中的最后一个元素后退出。 因此,我们将条件设置为“循环索引需要小于切片的长度”。 最后,我们将步进功能设置为“将索引增加1”。
To be noted, all these three short statements are optional. We will see its different implications below.
需要注意的是,所有这三个简短语句都是可选的。 我们将在下面看到其不同的含义。
While form
虽然形式
There’s no while
keyword in Go. While form is just a variant of the basic for
loop. If we omit the initiation and stepping function from the basic form, we will get the while form. Therefore, as long as the loop condition is met, the loop will keep executing. The example above can be easily transformed to while form like this.
Go中没有while
关键字。 而形式只是基本的for
循环的变体。 如果从基本形式中省略启动和步进功能,则将获得while形式。 因此,只要满足循环条件,循环就会继续执行。 上面的示例可以很容易地转换成while形式,就像这样。
Endless form
无休止的形式
Endless loop, also known as “dead loop”, is a dangerous loop. Normally, if we have an endless loop, it’s going to break the program because it can devour all the resources the program can get which can crash the host system as a result. That’s normally a very bad thing. However, in Go, there are some special use cases where we can use endless loops for good. For instance, when you want to run an async background process, you can create an endless loop and let it running in background. I promise, that won’t yield a catastrophic outcome. To write an endless loop, just omit all three statements in the basic form.
无限循环,也称为“死循环”,是一个危险的循环。 通常,如果我们有一个无限循环,它将破坏程序,因为它会吞噬程序可以获取的所有资源,从而使主机系统崩溃。 这通常是一件非常糟糕的事情。 但是,在Go中,有一些特殊的用例,我们可以永久使用无限循环。 例如,当您要运行异步后台进程时,可以创建一个无限循环,并使其在后台运行。 我保证,这不会带来灾难性的结果。 要编写一个无限循环,只需忽略基本形式的所有三个语句。
Of course, I’m not going to break your computer by asking you to run an endless loop directly in the main function. What we are doing here is to run it in a goroutine (will be covered in future). Goroutine enables async processing, hence our endless loop runs in parallel of the main process. While the main process sleeps for 5 seconds, the async process should execute the endless loop continuously until the main process exits. That’s why we see “I’m an endless loop.” printed 5 times in the console.
当然,我不会要求您直接在main函数中运行无限循环来破坏您的计算机。 我们在这里要做的是在goroutine中运行它(将来会介绍)。 Goroutine启用了异步处理,因此我们的无限循环与主进程并行运行。 当主进程Hibernate5秒钟时,异步进程应连续执行无限循环,直到主进程退出。 这就是为什么我们看到“我是一个无尽的循环”的原因。 在控制台中打印5次。
Range operator
范围运算符
Last but not least, let’s talk about the range operator. It helps to easily iterate over types like slices/arrays, maps, channels and strings. Let’s see how we can simplify the basic form.
最后但并非最不重要的一点,让我们谈谈范围运算符。 它有助于轻松地遍历切片/数组,映射,通道和字符串等类型。 让我们看看如何简化基本形式。
When applying range operator on a slice or an array, it provides two variables for each iteration, namely the index and value of current iteration. Note that these variables are short-lived and scoped to each iteration, and they can only be accessed inside the loop. Usually, we don’t need the index, so we can omit assigning a variable by using _
. It will look like this for _, value := range slice {…}
. In fact, if you don’t use any of the variables inside the loop, Go will force you to omit them.
在切片或数组上应用范围运算符时,它为每次迭代提供两个变量,即当前迭代的索引和值。 请注意,这些变量是短暂的,范围仅限于每次迭代,并且只能在循环内部访问。 通常,我们不需要索引,因此我们可以省略使用_
来分配变量。 for _, value := range slice {…}
看起来像这样。 实际上,如果您在循环中不使用任何变量,Go会强制您忽略它们。
Iterating through a map using the range operator also returns two variables. Except, it returns keys instead of indexes for maps.
使用范围运算符遍历地图也将返回两个变量。 除此以外,它返回键而不是地图的索引。
Similarly, we need to omit any variables returned by the range operator if we don’t use them.
同样,如果不使用范围运算符,则需要忽略它们所返回的任何变量。
Range operator returns two variables when applied on a string, or String type. The first one is the same as slice/array, which is an index. The second one is the Unicode (represented by int32
) of the character.
当应用于字符串或String类型时,范围运算符返回两个变量。 第一个与片段/数组相同,后者是一个索引。 第二个是字符的Unicode(用int32
表示)。
In this example, you can see that we used a new function from the fmt
package. It’s time to explain the formatting capabilities of the fmt
package. You can skip to the last section to have a sneak peek.
在此示例中,您可以看到我们使用了fmt
包中的新函数。 现在是时候解释fmt
软件包的格式化功能了。 您可以跳到最后一部分以进行偷看。
continue & break
继续并中断
continue
and break
are the short-circuit statements for skipping iterations. When using continue
, the program will skip the code execution after continue
and start the next iteration. While break
is more forceful, the program will skip the entire loop when it sees break
.
continue
和break
是跳过迭代的短路语句。 当使用continue
,程序将在continue
之后跳过代码执行并开始下一个迭代。 尽管break
更具强制性,但程序在看到break
时将跳过整个循环。
goto & label
转到和标签
goto
and label
enable jumping from a statement to another line of code in the same function. The convention of label names is to use all capital letters.
goto
和label
允许从一条语句跳转到同一函数中的另一行代码。 标签名称的惯例是使用所有大写字母。
goto
and label
don’t have the best reputation out there. Misusing them can be a sign of spaghetti code, which is something everyone wants to avoid. However, they are not natively evil. They do serve a specific purpose. For instance, in the built-in math package, we can find a proper use of goto
statements.
goto
和label
没有最好的声誉。 滥用它们可能是意大利面条代码的标志,这是每个人都想避免的事情。 但是,它们不是天生的邪恶。 它们确实有特定的目的。 例如,在内置的数学包中,我们可以找到对goto
语句的正确使用。
defer
推迟
defer
is the last flow control statement I want to cover in this tutorial. It is used for deferring function execution until the closest surrounding function exits. Here is an example.
defer
是我要在本教程中介绍的最后一个流控制语句。 它用于推迟执行功能,直到最接近的周围功能退出。 这是一个例子。
As shown in the example, the statements after defer
were executed in the end when exiting the main function. To be noted, the execution sequence of a series of defer
statements is reversed. In other words, the last stated defer
will execute first when exiting the function. Also, the arguments inside defer
statements are evaluated inline. That is the only thing which is not deferred.
如示例所示, defer
之后的语句在退出main函数时最终执行。 要注意的是,一系列defer
语句的执行顺序是相反的。 换句话说,最后一个声明的defer
将在退出函数时首先执行。 另外, defer
语句中的参数是内联求值的。 那是唯一不被推迟的事情。
defer
is useful when we create something that is better to be closed or removed by the end of a function. Without defer
, we need to write an open statement in the beginning and then close it in the end. This is error-prone as it’s very easy to forget closing it. With defer
, we can write a defer Close()
statement right after opening. That way, it’s less likely to forget. We will use it when touching on database connections and HTTP servers in the advanced topics.
当我们创建更好地在函数结束时关闭或删除的内容时, defer
很有用。 如果没有defer
,我们需要在开始时编写一个open语句,然后在最后结束。 这很容易出错,因为很容易忘记关闭它。 使用defer
,我们可以在打开后立即编写defer Close()
语句。 这样,就不太可能忘记。 当涉及高级主题中的数据库连接和HTTP服务器时,我们将使用它。
奖励内容: fmt
软件包 (Bonus content: fmt
package)
![Image for post](https://i-blog.csdnimg.cn/blog_migrate/bdbc832b781184be29f76365d827d761.png)
I’m sure you have seen a lot of fmt.Println
functions. We used it a lot to print results in the console. fmt
is a powerful built-in formatting package of Go. Println
is one of the functions in the package. You can find the documentation of the fmt
package here. There are quite a few contents there. I’m not going to go through them one by one in this tutorial. What I want to point out is the formatter in this package. Using formatter can save us time and lines of code when manipulating strings.
我确定您已经看过很多fmt.Println
函数。 我们在控制台中大量使用它来打印结果。 fmt
是Go的强大内置格式化软件包。 Println
是软件包中的功能之一。 您可以在此处找到fmt
软件包的文档。 那里有很多内容。 在本教程中,我不会一一介绍它们。 我要指出的是此程序包中的格式化程序。 使用格式化程序可以在处理字符串时为我们节省时间和代码行。
You will find some of the functions have a trailing “f” in the function name. This is an indication that those functions accept formatter as parameters. So how do we use the formatter? Let’s check out this example of Printf
function.
您会发现某些函数在函数名称中带有尾随的“ f”。 这表明这些函数接受格式程序作为参数。 那么我们如何使用格式化程序呢? 让我们看看这个Printf
函数的例子。
The first parameter in Printf
is the format string and the following parameters are the arguments that will replace the format “verbs”. In the example above, format verb %s
is replaced by the whoami
value. You can place as many arguments as you want after the format string. However, the only requirement is the number of format verbs needs to match the number of arguments.
Printf
的第一个参数是格式字符串,以下参数是将替换格式“动词”的参数。 在上面的示例中,格式动词%s
替换为whoami
值。 您可以在格式字符串后放置任意数量的参数。 但是,唯一的要求是格式动词的数量需要与参数的数量匹配。
The core of this formatter lies in the format verbs, let’s check out different types of format verbs below, if you want to see the full list of format verbs, you can find them in the documentation of fmt
package.
该格式化程序的核心在于格式动词,让我们在下面查看格式动词的不同类型,如果要查看格式动词的完整列表,可以在fmt
软件包的文档中找到它们。
布尔值 (booleans)
The only available format verb for boolean is %t
, it will format the boolean value to string value (“true” or “false”).
布尔值唯一可用的格式动词是%t
,它将布尔值格式化为字符串值(“ true”或“ false”)。
数字(numbers)
Numbers have more format verbs. Here are some of the most widely used ones.
数字具有更多的格式动词。 以下是一些使用最广泛的工具。
%b
: base 2 number%b
:以2为底的数字%d
: base 10 number%d
:以10为底的数字%U
: Unicode format%U
:Unicode格式%e
/%E
: scientific notation%e
/%E
:科学计数法%f
: decimal point number%f
:小数点编号
Let’s see them in action.
让我们看看它们的作用。
Note: the format string wrapped with back-ticks are the same as normal double quotes. However, back-tick allows writing a single string in multiple lines.
注意:用反引号引起来的格式字符串与普通的双引号相同。 但是,反引号允许在多行中写入单个字符串。
弦 (strings)
Format verbs for strings:
格式化字符串的动词:
%s
: string%s
:字符串%q
: string wrapped with double quotes, safely escaped%q
:用双引号引起来的字符串,已安全转义
一般(General)
The verbs mentioned above are for specific types. There are also generic format verbs which work on any types.
上面提到的动词是针对特定类型的。 也有适用于任何类型的通用格式动词。
%v
`: value in default format%v
v`:默认格式的值%+v
: prints field names when value is a struct%+v
:当值是结构时打印字段名称%#v
: prints Go-syntax representation of the value%#v
:打印值的Go语法表示形式
More often, we don’t really need to print out strings in the console. We only need to format strings and use them in the program. Luckily, there’s another function called Sprintf
. It’s the same as Printf
, the only difference is Sprintf
returns formatted string back as a variable.
通常,我们实际上不需要在控制台中打印出字符串。 我们只需要格式化字符串并在程序中使用它们。 幸运的是,还有另一个名为Sprintf
的函数。 与Printf
相同,唯一的区别是Sprintf
将格式化后的字符串作为变量返回。
下一步是什么? (What’s next?)
![Image for post](https://i-blog.csdnimg.cn/blog_migrate/2fc1035b755a185df431322cb4a7aef0.png)
In the next tutorial, we will talk about pointers and functions. Stay tuned and I will see you in the next one.
在下一个教程中,我们将讨论指针和函数。 请继续关注,下一个会见。
Please feel free to leave a comment below if you run into any problems or if you need a helping hand. Feedback is always welcome. Thank you for reading!
如果您遇到任何问题或需要帮助,请随时在下面发表评论。 始终欢迎反馈。 感谢您的阅读!
A special thanks to Mark Hume-Cook for reviewing this tutorial.
特别感谢Mark Hume-Cook审阅了本教程。
翻译自: https://levelup.gitconnected.com/road-to-go-pro-flow-control-dfcc7b9a5395
财富之旅