本文始发于个人公众号: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语言入门教程系列——函数、循环与分支的更多相关文章

  1. Go语言入门教程(十)之函数

    Hello 各位小伙伴大家好,我是小栈君,假期一眨眼就过去了.不知道大家玩的是否开心呢? 上次我们讲到了关于Go语言的流程控制,小栈君也希望小伙伴跟着小栈君一步一个脚印的敲一下代码,相互进步.本期我们 ...

  2. 智能合约语言 Solidity 教程系列3 - 函数类型

    Solidity 教程系列第三篇 - Solidity 函数类型介绍. 写在前面 Solidity 是以太坊智能合约编程语言,阅读本文前,你应该对以太坊.智能合约有所了解,如果你还不了解,建议你先看以 ...

  3. 智能合约语言 Solidity 教程系列10 - 完全理解函数修改器

    这是Solidity教程系列文章第10篇,带大家完全理解Solidity的函数修改器. Solidity系列完整的文章列表请查看分类-Solidity. 写在前面 Solidity 是以太坊智能合约编 ...

  4. 《JavaScript语言入门教程》记录整理:入门和数据类型

    目录 入门篇 js介绍 历史 基本语法 数据类型 概述 null 和 undefined 数值 字符串 对象 函数 数组 本系列基于阮一峰老师的<JavaScrip语言入门教程>或< ...

  5. 《JavaScript语言入门教程》记录整理:运算符、语法和标准库

    目录 运算符 算数运算符 比较运算符 布尔运算符 二进制位运算符 void和逗号运算符 运算顺序 语法 数据类型的转换 错误处理机制 编程风格 console对象和控制台 标准库 Object对象 属 ...

  6. 《JavaScript语言入门教程》记录整理:面向对象

    目录 面向对象编程 实例对象与 new 命令 this关键字 对象的继承 Object对象的方法 严格模式(strict mode) 本系列基于阮一峰老师的<JavaScrip语言入门教程> ...

  7. WPF入门教程系列(二) 深入剖析WPF Binding的使用方法

    WPF入门教程系列(二) 深入剖析WPF Binding的使用方法 同一个对象(特指System.Windows.DependencyObject的子类)的同一种属性(特指DependencyProp ...

  8. 《Ruby语言入门教程v1.0》学习笔记-01

    <Ruby语言入门教程v1.0> 编著:张开川 邮箱:kaichuan_zhang@126.com 想要学习ruby是因为公司的自动化测试使用到了ruby语言,但是公司关于ruby只给了一 ...

  9. WPF入门教程系列二——Application介绍

    一.Application介绍 WPF和WinForm 很相似, WPF与WinForm一样有一个 Application对象来进行一些全局的行为和操作,并且每个 Domain (应用程序域)中仅且只 ...

随机推荐

  1. AJ学IOS(19)UI之QQ好友列表

    AJ分享,必须精品 先看效果图 哈哈,这次猫猫给来个动态的图片,这个看起来带劲 实现思路 首先建立模型 这里用到的是一个双层的模型. cell的实现 这里一看其实就知道是一个tableView,我们自 ...

  2. Pet BFS

    一天早上小明醒来时发现他的宠物仓鼠不见了. 他在房间寻找但是没找到仓鼠. 他想用奶酪诱饵去找回仓鼠. 他把奶酪诱饵放在房间并且等待了好几天. 但是可怜的小明除了老鼠和蟑螂没见到任何东西. 他找到学校的 ...

  3. 为什么选择python?

    Why python? 那些最好的程序员不是为了得到更高的薪水或者得到公众的仰慕而编程,他们只是觉得这是一件有趣的事情. —— Linux 之父 Linux Torvalds 作为一个使用主义的学习者 ...

  4. Springboot:员工管理之首页(十(2))

    访问首页可以通过两种方式: 1:编写controller 2:自定义扩展视图解析器(推荐使用) 1:编写Controller com\springboot\controller\IndexContro ...

  5. [半翻] 设计面向DDD的微服务

    这篇文章行文结构对照微软博客, 结合本人意译和多年实践的回顾思考形成此次读书笔记. Domian-driven Design 领域-驱动-设计(DDD)提倡基于(用例相关的现实业务)进行建模. 1. ...

  6. GoJS事件

    1.阻止键盘事件 myDiagram.commandHandler.doKeyDown = function () { var e = myDiagram.lastInput; // Meta(Com ...

  7. [Inno Setup] 安装完成后调用函数

    如果使用了通配符,每拷贝一个文件,函数都会被调用一次. Source: "path\test.exe"; DestDir: {app}; AfterInstall: LoadPer ...

  8. Python不同版本打包程序为.exe文件

    安装pyinstaller 测试用的python环境是3.6.2版本 下载地址 https://github.com/pyinstaller/pyinstaller/ 1.打开cmd,切到pyinst ...

  9. 为什么要你们现在要学习python

    说学习python之前,我们先来聊聊其他的.我们都认为成功靠的是勤奋和努力,但是事实是只靠勤奋和努力是不一定会成功的,而且很大一部分都不会成功. 你有没有想过,同样是做企业,有些公司年收入百万,而腾讯 ...

  10. 查看 Nginx 的日志目录

    即便是 docker 容器,对应的目录也是一样的 > /var/log/nginx/xxx.log