作为一门高级语言,Go同样提供了流程控制的支持。在了解了基础结构之后,继续学习Go的流程控制,里面涉及到的基础结构的内容还能对其有更多的了解。
<!--more-->
说流程控制之前先说一下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类型。具体的类型转换下面会详细说明。
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感觉像是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,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 第一眼看到可能会想到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'获取。
首发微信公众号:Go技术栈,ID:GoStack
版权归作者所有,任何形式转载请联系作者。
作者:搜云库技术团队
出处: https://gostack.souyunku.com/...