Go语言入门教程系列——函数、循环与分支
本文始发于个人公众号:TechFlow,原创不易,求个关注
今天是Golang专题的第四篇,这一篇文章将会介绍golang当中的函数、循环以及选择判断的具体用法。
函数
在之前的文章当中其实我们已经接触过函数了,因为我们写的main函数本质上也是一个函数。只不过由于main函数没有返回值,也没有传参,所以省略了很多信息。
func main() {
fmt.Println("Hello World")
}
下面,我们来看看一个完整的函数是怎样的,这是golang官网上的例子。
func add(x int, y int) int {
return x + y
}
这是一个非常简单的a+b的函数,我想大家应该都能看懂。我们来重点关注一下函数的格式。首先是func关键字,我们使用这个关键字定义一个函数,之后跟着的是函数名,然后是函数的传参,最后是函数的返回值。
这个顺序可能和我们之前普遍接触的语法不太一样,例如C++当中是把函数返回类型写在最前面,然后是函数名和传参。再比如Python当中则是没有返回值的任何信息,只有def关键字和函数名以及传入的参数。
golang有些像是Python和C++的综合体,总体来说我觉得内涵上更接近C++,但是写法上和Python更接近一些。
我们理解了函数的定义之后,下面来看看golang当中支持的一些特性。
变量简写
在变量声明的时候,我们如果定义两个相同类型的变量是可以把它们进行缩写的。比如我们定义两个int类型的变量,分别叫做a和b。那么可以简写成这样:
var a, b int
同样,在函数当中,如果传入的参数类型相同,也一样是可以简写的。我们可以把x和y两个参数缩写在一起,用逗号分开,共享变量类型。
func add(x, y int) int {
return x + y
}
多值返回
在前面介绍golang特性的时候曾经提到过,golang作为一个看起来很守旧的语言,但是却支持很多新鲜的特性。其中最知名的一个特性就是函数支持多值返回,即使是现在,也只有少量的语言支持这一特性。
在许多语言当中,如果需要返回多个值,往往需要用一个结构体或者是tuple、list等数据结构将它们包装起来。但是在golang当中支持同时返回多个结果,这将会极大地方便我们的编码。
func sample() (string, string) {
return "sample1", "sample2"
}
多值返回也会有一个小小的问题,就是如果我们要返回的值过多,会导致这个return会写得很长,或者是组装的逻辑变得很复杂。或者是很容易产生遗漏、搞混顺序之类的问题,golang当中针对这个问题也进行优化,支持我们对返回值进行命名。当命名的变量赋值完成之后,我们就可以直接用return关键字返回所有数据。
这个操作很难用语言描述很清楚,我们来看下面的例子:
func sample(x, y, z int) (xPrime, yPrime, zPrime int) {
xPrime, yPrime, zPrime = x-1, y+1, z-2
return
}
在上面的代码当中,在返回之前,我们先给要返回的值起好了名字,我们在函数体当中对这些值进行赋值完成之后,我们就可以直接return了,golang会自动将它们的值填充进行返回。这样不但可以简化一定的编码过程,也可以增加可读性。
defer
golang的函数当中有一个特殊的用法,就是defer。这个用法据说其他语言也有,但是我暂时没有见到过。defer是一个关键字,用它修饰的语句会被存入栈中,直到函数退出的时候执行。
比如:
func main() {
defer fmt.Println("world")
fmt.Println("hello")
}
上面这两行代码虽然defer的那一行在先,但是并不会被先执行,而是等main函数执行退出之前才会执行。
看起来这个用法有一点点怪,但是它的用处很大,经常用到。比如当我们打开一个文件的时候,不管文件有没有打开成功,我们都需要记得关闭文件。但如果文件打开不成功可能就会有异常或者是报错,如果我们把这些情况全部都考虑到,会变得非常复杂。所以这个时候我们通常都会用defer来执行文件的关闭。
要注意的是,defer修饰的代码会被放入栈中。所以最后会按照先进后出的原则进行执行。比如:
func main() {
for i := 0; i < 10; i++ {
defer fmt.Println(i)
}
fmt.Println("done")
}
最后执行的结果是9876543210,而不是相反。这一点蛮重要的,有的时候如果搞混了,很容易出现问题。
循环
和其他语言不同,Golang当中只有一种循环,就是for循环。没有while,更没有do while循环。在golang的设计中设想当中,只需要一种循环,就可以实现所有的功能。从某种程度上来说,也的确如此,golang中的循环有点像是C++和Python循环的结合体,集合两种所长。
首先,我们先来看下for循环的语法,在for循环当中,我们使用分号分开循环条件。循环条件分为三个部分,第一个部分是初始化部分,我们对循环体进行初始化,第二个部分是判断部分,判断循环结束的终止条件,第三个部分是循环变量的改变部分。
写出来大概是这样的:
for i := 0; i < 10; i++ {
fmt.Println(i)
}
这个语法是不是和C++中的循环很像呢?可以说除了没有括号之外,基本上就是一样的。golang当中同样支持++的自增操作,不过golang中只支持i++,而不支持++i。
和C++一样,这三段当中的任何一段都是可以省略的,比如我们可以省略判断条件:
for i := 0; ; i++ {
fmt.Println(i)
if i > 10 {
break
}
}
我们也可以省略循环变量的自增条件:
for i := 0; i < 10; {
i += 2
fmt.Println(i)
}
甚至可以全部省略,如果全部省略的话,等价于C++中的while(true)循环,也就是死循环。
range的用法
如果我们用循环遍历一个数组或者是map,它的这个用法和Python中的用法非常类似。我们来看下,假如我们有一个数组是:
nums := []int{2, 3, 4}
sum := 0
for i, v := range nums {
sum += v
fmt.Println(i)
}
这个用法等价于Python中的for i, v in enumerate(nums)。也就是通过range会同时返回数组和map中的下标与对应的值,我们再来看下map,其实也是一样的。
kvs := map[string]string{"a": "apple", "b": "banana"}
for k, v := range kvs {
fmt.Printf("%s -> %s\n", k, v)
}
如果你看不懂map和数组的定义没有关系,我们会在之后的文章当中再来详细讲解,这篇的主要内容是循环。我们只需要看得懂for循环的range操作即可。
判断
golang当中支持if与switch进行条件判断。我们先来看if,在golang当中的if和Python比较接近,在if的判断条件外面不需要加上小括号(),但是if的执行条件当中必须要大括号{},即使只有一行代码。
比如刚才我们写的循环中的那个break。
for i := 0; ; i++ {
fmt.Println(i)
if i > 10 {
break
}
}
在判断中初始化
上面的逻辑在各个语言中都大同小异,很多语言都是这么写的。但是golang对于if还有特殊的支持,golang支持在if条件当中加上初始化信息。
比如:
if v := sample(); v < 10 {
fmt.Println(v)
}
上面当中的v是在if执行的时候才进行的初始化,也就是说我们将变量的初始化和if判断结合在了一起。这个用法非常重要,在golang当中也大规模使用,所以我们一定要学会这个用法。
switch
golang当中也支持switch用法,它的基本套路和C++一样,但是在细微的地方又做了优化。
比如和if一样,switch也支持在执行的时候初始化。比如:
switch flag := sample(); flag {
case "a":
fmt.Println(flag)
case "b":
fmt.Println(flag)
default:
fmt.Println(flag)
}
看明白了吗,代码当中的flag是我们执行switch的时候才创建出来的。分号之前的都是初始化的代码,分号之后的表达式才是switch进行判断的内容。
还有一个小细节需要注意,在golang当中使用switch的时候,每个case的判断条件后面不需要再加上break。我们在写其他语言的时候,如果用到switch要么就是忘记了case的执行条件后面要加上break,要么就是写很多break非常麻烦。golang的设计者觉得每个case都加上break太二了,因为大家基本上都只用switch执行一个case,所以就去掉了必须要加上break这个设定。
switch执行顺序
在golang当中,switch的判断条件按照顺序执行。
为什么要强调这个呢?因为你很有可能会看到有些人的代码里的switch没有判断条件,比如:
switch a := sample();{
case a < 5:
fmt.Println(a)
case a > 5:
fmt.Println(a)
default:
fmt.Println("end")
}
在上面这段代码当中,我们根本没有为switch设置判断的根据,这段逻辑完全等同于若干个if-else条件的罗列,它在golang当中同样是允许的。
题外话
今天本来是分布式专题,但实在是没有想到什么很好的题目,我也不喜欢强求,干脆就换个主题吧。以后分布式专题还会更新,不过可能要改成间歇式的了,后面想少写点理论,能够分享一点可以实际用上的东西(所以需要的时间比较久)。
不知道大家从今天的内容当中有没有感受到golang这门语言的个性,很多地方看起来中规中矩,却又能创造出新的用法来,至少我是很佩服设计者的想法的。golang当中这些新特性初见的时候往往会觉得不喜欢和排斥,怎么看怎么怪异,但是写多了之后还是蛮香的。
今天的文章就到这里,扫码关注,获取更多优质文章。
各位看官大大,赏赐个关注再走吧~
Go语言入门教程系列——函数、循环与分支的更多相关文章
- Go语言入门教程(十)之函数
Hello 各位小伙伴大家好,我是小栈君,假期一眨眼就过去了.不知道大家玩的是否开心呢? 上次我们讲到了关于Go语言的流程控制,小栈君也希望小伙伴跟着小栈君一步一个脚印的敲一下代码,相互进步.本期我们 ...
- 智能合约语言 Solidity 教程系列3 - 函数类型
Solidity 教程系列第三篇 - Solidity 函数类型介绍. 写在前面 Solidity 是以太坊智能合约编程语言,阅读本文前,你应该对以太坊.智能合约有所了解,如果你还不了解,建议你先看以 ...
- 智能合约语言 Solidity 教程系列10 - 完全理解函数修改器
这是Solidity教程系列文章第10篇,带大家完全理解Solidity的函数修改器. Solidity系列完整的文章列表请查看分类-Solidity. 写在前面 Solidity 是以太坊智能合约编 ...
- 《JavaScript语言入门教程》记录整理:入门和数据类型
目录 入门篇 js介绍 历史 基本语法 数据类型 概述 null 和 undefined 数值 字符串 对象 函数 数组 本系列基于阮一峰老师的<JavaScrip语言入门教程>或< ...
- 《JavaScript语言入门教程》记录整理:运算符、语法和标准库
目录 运算符 算数运算符 比较运算符 布尔运算符 二进制位运算符 void和逗号运算符 运算顺序 语法 数据类型的转换 错误处理机制 编程风格 console对象和控制台 标准库 Object对象 属 ...
- 《JavaScript语言入门教程》记录整理:面向对象
目录 面向对象编程 实例对象与 new 命令 this关键字 对象的继承 Object对象的方法 严格模式(strict mode) 本系列基于阮一峰老师的<JavaScrip语言入门教程> ...
- WPF入门教程系列(二) 深入剖析WPF Binding的使用方法
WPF入门教程系列(二) 深入剖析WPF Binding的使用方法 同一个对象(特指System.Windows.DependencyObject的子类)的同一种属性(特指DependencyProp ...
- 《Ruby语言入门教程v1.0》学习笔记-01
<Ruby语言入门教程v1.0> 编著:张开川 邮箱:kaichuan_zhang@126.com 想要学习ruby是因为公司的自动化测试使用到了ruby语言,但是公司关于ruby只给了一 ...
- WPF入门教程系列二——Application介绍
一.Application介绍 WPF和WinForm 很相似, WPF与WinForm一样有一个 Application对象来进行一些全局的行为和操作,并且每个 Domain (应用程序域)中仅且只 ...
随机推荐
- 运输层--------运输层与网络层的关系、UDP、TCP
一.运输层与网络的区别: 网络层提供了主机之间的逻辑通信,而运输层为运行在不同主机上的进程之间提供了逻辑通信 二.实例证明: 考虑有两个家庭,一家位于美国东岸,一家位于美国西海岸,每家有12孩子.东海 ...
- Daily Scrum 12/29/2015
Process: Zhaoyang: Add the Time bar feature in the APP and complete the Speech API. Yandong: Do some ...
- stand up meeting 1/13/2016
part 组员 工作 工作耗时/h 明日计划 工作耗时/h UI 冯晓云 UI测试和调整:与主程序完成合并 6 查漏补缺,扫除UI ...
- react: typescript project initialize
Initialize the project create a folder project Now we’ll turn this folder into an npm package. npm i ...
- 用long类型让我出了次生产事故,写代码还是要小心点
昨天发现线上试跑期的一个程序挂了,平时都跑的好好的,查了下日志是因为昨天运营跑了一家美妆top级淘品牌店,会员量近千万,一下子就把128G的内存给爆了,当时并行跑了二个任务,没辙先速写一段代码限流,后 ...
- 《HelloGitHub》第 49 期
兴趣是最好的老师,HelloGitHub 就是帮你找到兴趣! 简介 分享 GitHub 上有趣.入门级的开源项目. 这是一个面向编程新手.热爱编程.对开源社区感兴趣 人群的月刊,月刊的内容包括:各种编 ...
- ajax后台返回指定的错误码
js: $.ajax({ type: "POST", url: 'post.php', data: serialNumber + "&getSerialNumbe ...
- 20199326《Linux内核原理与分析》第十二周作业
Collabtive系统跨站请求伪造攻击实验 实验背景 CSRF(Cross-site request forgery),中文名称:跨站请求伪造,也被称为:one click attack/sessi ...
- REDHAT7进入单用户模式
Redhat7采用的是grub2,和Redhat6.x进入单用户的方法不同. 一.init方法 1.centos7的grub2界面会有两个入口,正常系统入口和救援模式: 2.修改grub2引导 在正常 ...
- cocos2dx初体验
我们创建工程后总会自带一个HelloWorld类,短短的几行代码就出来了一个游戏的雏形,请问我们真的理解它了吗?如果我们能早一点弄明白这几行代码,我们或许会比现在走得更远. 理解HelloWorld类 ...