Golang入门教程——函数、循环与分支

本文始发于我的公众号:TechFlow,原创不易,求个关注golang


今天是Golang专题的第四篇,这一篇文章将会介绍golang当中的函数、循环以及选择判断的具体用法。web

函数

在以前的文章当中其实咱们已经接触过函数了,由于咱们写的main函数本质上也是一个函数。只不过因为main函数没有返回值,也没有传参,因此省略了不少信息。数组

func main() {
 fmt.Println("Hello World")
}
复制代码

下面,咱们来看看一个完整的函数是怎样的,这是golang官网上的例子。数据结构

func add(x int, y int) int {
    return x + y
}
复制代码

这是一个很是简单的a+b的函数,我想你们应该都能看懂。咱们来重点关注一下函数的格式。首先是func关键字,咱们使用这个关键字定义一个函数,以后跟着的是函数名,而后是函数的传参,最后是函数的返回值。app

这个顺序可能和咱们以前广泛接触的语法不太同样,例如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当中这些新特性初见的时候每每会以为不喜欢和排斥,怎么看怎么怪异,可是写多了以后仍是蛮香的。

今天的文章就到这里,扫码关注,获取更多优质文章。

相关文章
相关标签/搜索