【跟着我们学Golang】流程控制

栏目: Java · 发布时间: 6年前

内容简介:作为一门高级语言,Go同样提供了流程控制的支持。在了解了基础结构之后,继续学习Go的流程控制,里面涉及到的基础结构的内容还能对其有更多的了解。<!--more-->说流程控制之前先说一下interface,因为后续在流程控制中会穿插着对interface的使用。

作为一门高级语言,Go同样提供了流程控制的支持。在了解了基础结构之后,继续学习 Go 的流程控制,里面涉及到的基础结构的内容还能对其有更多的了解。

<!--more-->

说流程控制之前先说一下interface,因为后续在流程控制中会穿插着对interface的使用。

interface

interface是一切类型的基类型,类似于 Java 中的基类 Obejct ,所有的结构都是interface的实现,因为interface基类型没有定义任何的函数,所以其他任何结构都认为是interface的实现。当然,也可以自己定义interface自己去实现相应的函数,这个下期面向对象的时候会详细解释。这里先简单说明interface作为基类型时的使用。

在Java中,所有的类型都是Object的子类,所以声明对象时可以将对象的类型声明为Object,在赋值时给一个子类型,在Go中同样可以,但仅限于针对interface声明的使用(还是会牵涉到面向对象的东西),也就是说,声明时可以将变量声明为interface类型,赋值时给一个其他基础类型的值,这是最简单的interface作为基类型的使用。

var hello interface{} = "hello world"

fmt.Println(hello)

例子中声明hello时,声明的类型是interface{}类型,并不是string类型,但是赋值时给的是string类型,说明hello实际类型还是string类型。具体的类型转换下面会详细说明。

if-else

Go中的if-else结构的用户与Java中的特别的类似,仅仅区别在两者的语法上面,Go的语法为:

if 条件1 {
    ...
} else if 条件2 && 条件3 {
    ...
} else {
    ...
}

Go对语法的要求没有Java那么严格,对于括号可以带,也可以不带。同样的,Go也支持 &&||! 这样的运算符进行多个条件的关联判断

func max(a, b int) (max int) {
    if a > b {
        max = a
    } else if a == b {
        max = a
    } else {
        max = b
    }
    
    return 
}

断言

断言在Go中是一种类型转换的语法,能否方便的进行类型的转换。Go语言中简单的断言语法为 value := element.(type)

//value := element.(type) //type为要转换的类型

var hello interface{} = "helloworld"

fmt.Println(hello.(string))
fmt.Println(hello.(int))//该行会报错,因为hello实际类型是string类型

稍微不注意,直接转换的话就会出现异常,所以一般不推荐使用简单的语法,而是用高级语法 value, ok := element.(type) ,这也是在if-else结构中讲解的原因。

// value, ok := element.(type) //type为要转换的类型,ok为是否成功转换,类型为bool,value为实际转换的值

var hello interface{} = "helloworld"

helloS, ok := hello.(string)
if ok {
    fmt.Println("hello tranfer successfully : ", helloS)
} else {
    fmt.Println("hello transfer failed")
}

使用高级语法能保证在运行的时候不会出现错误,保证程序的持续执行,这是比较推荐的做法。

map断言是map的一种高级用法。

//map的断言
// value, ok := m[key] //这里的OK不再是简单的成功或者失败,理解成是否存在更合适
var m = make(map[string]interface{})//创建map的方式,具体make的用法后续会讲解

m["key1"] = "value1"
value1, ok := m["key1"]
if ok {
    fmt.Println("map m contain 'key1' ", value1)
} else {
    fmt.Println("map m contain 'key1'")
}

map在断言的使用上好像是天生支持似的,不需要进行Contains函数的校验等,直接使用,平时在代码中使用的也是非常多。简直不要太好用。

switch

switch感觉像是if-else的高级版,同样是进行条件判断的结构,不同的条件执行不同的语句。语法类似Java,Java中只能使用byte、int、short、char和string,在Go中可没有这些限制。

从上至下的判断,直到找到匹配的case或者执行default语句,case结尾也不需要break进行跳出流程操作,执行完自动跳出。相反,如果想执行下一个case的话,需要使用 fallthrough 关键字进行下沉操作,

这时候下一条case的条件将被忽略。

switch value1 { //大括号必须与switch保持一行
    case value1:
        ...
    case value2, value3://多个条件使用逗号隔开
        ...
    default://没有符合的条件执行默认
        ...
}

语法规定 switch后跟的value1可以是任意类型(甚至是不写),但是case后的条件必须和switch后的value保持相同类型

grade := 10
switch grade {
//case code < 60://code为int类型,不能使用code < 60作为case条件
case 10:
    fmt.Println("不及格")
case 70:
    fmt.Println("及格")
default:
    fmt.Println("无效的分数")
}

//用于类型断言
switch hello.(type) {
case string:
    fmt.Println("hello is string")
case int:
    fmt.Println("hello is int")
default:
    fmt.Println("hello is unknown type")
}

switch {//直接判断case
case a < b:
    fmt.Println("a less than b")
    fallthrough //紧接着执行下一个case,不需要进行判断
case a > b:
    fmt.Println("a bigger than b")
}

for

说到循环、重复执行等首先想到的就是for,Go同样提供了支持,相对于Java,Go中for的使用更灵活。

同样的,想跳出for循环时使用 break 关键字。

//语法一
for init;条件;赋值{//左侧大括号必须与for同行
    ...
}

//语法二
for 条件 {//左侧大括号必须与for同行
    ...
}

//语法三
//这是个死循环
for {//左侧大括号必须与for同行
    ...
}

//语法四
for index, value := range slice/array/map {//range是关键字
    ...
}

上手就是一个 排序 来介绍最基本的for结构

a := []int{1, 3, 9, 4, 1, 4, 6, 132, 1, 29, 43, 55, 89, 46}
for i := 0; i < len(a); i++ {//len为Go内置函数
    for j := i + 1; j < len(a); j++ {
        if a[i] > a[j] {
            a[i], a[j] = a[j], a[i]
        }
    }
}

fmt.Println(a)//结果:[1 1 1 3 4 4 6 9 29 43 46 55 89 132]

只写条件的for循环,类似Java中的while

var i = 0
for i < len(a) {
    fmt.Print(a[i],"  ")
    i++
}//结果: 1  1  1  3  4  4  6  9  29  43  46  55  89  132

死循环写法更简单了,不过需要注意使用break进行跳出,否则电脑就该嗡嗡嗡~响不停了

i = 0
for{
    if i < len(a) {
        fmt.Print(a[i], " ")
        i++
    } else {
        break
    }
}//结果: 1 1 1 3 4 4 6 9 29 43 46 55 89 132

最牛的语法四就是为slice和array使用的,能遍历所有的集合。当遍历slice和array时,index指的是其中的索引位置;遍历map时指的就是key了。请看下面的例子

for index, value := range a {
    fmt.Printf("index: %d, value: %d \n", index, value)
}
/*
结果:
index: 0, value: 1
index: 1, value: 1
index: 2, value: 1
index: 3, value: 3
index: 4, value: 4
index: 5, value: 4
index: 6, value: 6
index: 7, value: 9
index: 8, value: 29
index: 9, value: 43
index: 10, value: 46
index: 11, value: 55
index: 12, value: 89
index: 13, value: 132
 */
m := map[string]string{}
m["hello"] = "world"
m["hey"] = "bye"

for key, value := range m {
    fmt.Printf("key: %s, value: %s \n", key, value)
}
/*
结果:
key: hello, value: world
key: hey, value: bye
 */

select

select 第一眼看到可能会想到 SQL 中的选择,但是它也是Go中的一个流程控制关键字。

select的使用主要是结合channel来使用,所以这里要是讲解channels会设计到很多东西,我们后期会做详细的讲解,这里先做select的介绍。

select的语法跟switch类似,用于选择合适的条件进行执行相应的逻辑,但牵涉到channel,所以select中的case都是对channel的操作,只能是往channel中读或者写。

select {
    case channel读操作:
        ...
    case channel写操作:
        ...
    default:
        ...
}

注意点:

channel包含读和写两种操作,case中必须包含一种操作

case的执行是无序的、随机的,select会执行任意一个可执行的case

没有可执行的case时会执行default,没有default的话就会阻塞,等待可执行的channel

下面是一个简单的例子实现,先不要深究内容含义,了解select语法即可

c := make(chan int, 1)
select {
case c <- 1:
    fmt.Println("push into channel")
case <-c:
    fmt.Println("get from channel")
default:
    fmt.Println("default")
}
//结果:push into channel

...

不要怀疑标题,标题就是三个英文点,这里要说一下这三个点的问题,以此来解释一下为什么在使用fmt.Println()和fmt.Printf()函数时使用逗号将参数隔开的问题。

我们先看一下fmt.Println()和fmt.Printf()的源码

// Println formats using the default formats for its operands and writes to standard output.
// Spaces are always added between operands and a newline is appended.
// It returns the number of bytes written and any write error encountered.
func Println(a ...interface{}) (n int, err error) {
    return Fprintln(os.Stdout, a...)
}

// Printf formats according to a format specifier and writes to standard output.
// It returns the number of bytes written and any write error encountered.
func Printf(format string, a ...interface{}) (n int, err error) {
    return Fprintf(os.Stdout, format, a...)
}

这里看到Println()和Printf()这两个函数其实就一个入参,为什么我能用逗号分隔从而给多个参数呢?

原因是这样的, a ...interface{} 这个其实是slice的一个特殊用法,说明这定义的是一个 可变参数 ,可以接收不定数量的统一类型的参数,定义为...interfaec{}就可以接收不定数量的任意基础类型。定义可变参数时的语法就是在类型前面加上这三个点,这里使用interface就说明可以接收任何类型

想使用这可变参数的语法也很简单,可以将其作为slice使用,也可以继续将其作为可变参数使用。使用可变参数的语法就是在定义的后面加上这三个点。下面看例子

func main(){
    definedThreeDot("jack", "rose", "tom", "jerry")//定义多个参数来使用可变参数
}
func definedThreeDot(source ...string) {//定义可变参数,定义时在类型前面加上三个点
    useThreeDot(source...)//将可变参数作为可变参数使用,使用时在定义后面加上三个点
    useThreeDotAsSlice(source)//将可变参数作为slice使用
}

func useThreeDotAsSlice(ss []string) {//定义slice来接收可变参数
    fmt.Println(ss)//直接打印slice
}

func useThreeDot(ss ...string) {//定义可变参数,定义时在类型前面加上三个点
    for index, s := range ss {//作为slice来遍历可变参数
        fmt.Printf("index : %d, value : %s \n", index, s)//index和s都作为可变参数来使用
    }
}

/*
结果:
index : 0, value : jack 
index : 1, value : rose 
index : 2, value : tom 
index : 3, value : jerry 
[jack rose tom jerry]
*
/

总结

Go 中的流程控制大致上就这么多,平时项目中使用的也是非常多的,特别是对便利集合时,非常的方便。相信你亲自体验后也会赞不绝口的。

同时也顺带解释了一下可变参数,结合着slice和流程控制也能对这个可变参数有一个更深的了解。

源码可以通过'github.com/souyunkutech/gosample'获取。

关注我们的「微信公众号」

【跟着我们学Golang】流程控制

首发微信公众号:Go技术栈,ID:GoStack

版权归作者所有,任何形式转载请联系作者。

作者:搜云库技术团队

出处: https://gostack.souyunku.com/...


以上所述就是小编给大家介绍的《【跟着我们学Golang】流程控制》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

The Shallows

The Shallows

Nicholas Carr / W. W. Norton & Company / 2011-6-6 / USD 15.95

"Is Google making us stupid?" When Nicholas Carr posed that question, in a celebrated Atlantic Monthly cover story, he tapped into a well of anxiety about how the Internet is changing us. He also crys......一起来看看 《The Shallows》 这本书的介绍吧!

MD5 加密
MD5 加密

MD5 加密工具

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换