Go基础系列:流程控制结构

条件判断结构:if else
分支选择结构:switch case
循环结构:for
break:退出for或switch结构(以及select)
continue:进入下一次for迭代

虽然Go是类C的语言,但Go在这些流程控制语句中的条件表达式部分不使用括号。甚至有些时候使用括号会报错,但有些复杂的条件判断需要使用括号改变优先级。

如:

if (name == "longshuai" && age > 23) || (name == "xiaofang" && age < 22) {
    print("yeyeye!!!")
}

if语句

if condition1 {
    // do something
} else if condition2 {
    // do something else
} else {
    // catch-all or default
}

注意,Go对语法要求很严格。左大括号{必须和if、else或else if在同一行,右大括号}必须换行,如果有else或else if,则必须紧跟这两个关键字。也就是说,上面的代码结构中,大括号的使用位置是强制规范的,不能随意换行放置。

在Go中,if语句的condition前面可以加上初始化语句,例如Go中很常见的:

if val := 10; val > max {
    // do something
}

它在一定程度上等价于:

val := 10
if val > max {
    // do something
}

但注意,前面简写的方式中,val的作用域只在if范围内,if外面无法访问这个val。如果在if语句之前已经定义了一个val,那么这个val将被if中的val掩盖,直到if退出后才恢复。

func main() {
    val := 20
    if val := 10; val > 3 {
        println("true")
    }
    println(val)    // 输出20
}

一种解决方式是if中的初始化语句不要使用:=,而是直接使用=,但这样会修改原始的值。

func main() {
    val := 20
    if val = 10; val > 3 {
        println("true")
    }
    println(val)    // 输出10
}

在Go中,经常使用两个(或多个)返回值的函数,一个返回值作为值,另一个作为布尔类型的判断值,或者作为错误信息。通常会使用if语句去检测多个返回值的函数是否成功。

但注意,一般有两种判断返回值:一种是ok类型,一种是err类型的错误信息。前者是布尔值,后者是表明错误信息的字符串,如果没错误,则err为nil。

value,ok := func_name()
if !ok {
    // func_name执行错误
    os.Exit(1)
}

value,err := func_name()
if err != nil {
    // func_name执行错误
    os.Exit(1)
    // 或 return err
}

将上面的简写一下,得到更常见的判断方式:

if value,ok := func_name();ok {
    // ok为true,函数执行成功
} else {
    // ok为false,函数执行失败
    os.Exit(1)
}

if value,err := func_name();err != nil {
    // err不为nil,说明出现错误
    return err
    //或os.Exit(1)
} else {
    // err为空,说明执行正确
}

switch语句

switch语句用于提供分支测试。有两种swithc结构:expression switch和type switch,本文暂时只介绍expression switch,它用于判断表达式是否为true。

对于expression switch,也有三种形式:等值比较、表达式比较、初始化表达式。

等值比较结构:当var1的值为val1时,执行statement1,当var1的值为val2时,执行statement2,都不满足时,执行默认的语句statement。

switch var1 {
    case val1:
        statement1
    case val2:
        statement2
    default:
        statement
}

等值比较局限性很大,只能将var1和case中的值比较是否相等。如果想比较不等,或者其它表达式类型,可以使用下面的表达式比较结构。

表达式比较结构:评估每个case结构中的condition,只要评估为真就执行,然后退出(默认情况下)。

switch {
    case condition1:
        statement1
    case condition2:
        statement2
    default:
        statement
}

初始化表达式:可以和if一样为switch加上初始化表达式,同样作用域只在switch可见。但注意,initialization后面记得加上分号";"结尾。见下文示例。

switch initialization; {  // 不要省略分号
    case condition1:
        statement1
    case condition2:
        statement2
    defautl:
        statement
}

default是可选的,且可以写在switch的任何位置。

如果case中有多个要执行的语句,可以加大括号,也可以不加大括号。当只有一个语句的时候,statement可以和case在同一行。

case中可以提供多个用于测试的值,使用逗号分隔,只要有一个符合,就满足条件:

switch var1 {
    case val1,val2,val3:
        statement1
    case val4,val5:
        statement2
    default:
        statement
}

例如:

val := 20
switch val {
case 10, 11, 15:
    println(11, 15)
case 16, 20, 22:      // 命中
    println(16, 20, 22)
default:
    println("nothing")
}

即使是表达式比较结构,也一样可以使用逗号分隔多个表达式,这时和使用逻辑或"||"是等价的:

func main() {
    val := 21
    switch {
    case val % 4 == 0:
        println(0)
    case val % 4 == 1, val % 4 == 2:  //命中
        println(1, 2)
    default:
        println("3")
    }
}

默认情况下case命中就结束,所以所有的case中只有一个会被执行。但如果想要执行多个,可以在执行完的某个case的最后一个语句上加上fallthrough,它会无条件地直接跳转到下一条case并执行,如果下一条case中还有fallthrough,则相同的逻辑。此外,fallthrough的后面必须只能是下一个case或default,不能是额外的任何语句,否则会报错。

例如:

func main() {
    val := 21
    switch val % 4 {
    case 0:
        println(0)
    case 1, 2:         // 命中
        println(1, 2)  // 输出
        fallthrough    // 执行下一条,无需条件评估
        // println("sd") //不能加此行语句
    case 3:
        println(3)     // 输出
        fallthrough    // 执行下一条,无需条件评估
    default:
        println("end")  // 输出
    }
}

执行结果为:

1 2
3
end

fallthrough一般用于跳过某个case。例如:

swtich i {
    case 0: fallthrough
    case 1: statement1
    default: statement
}

它表示等于0或等于1的时候都执行statement1。这和前面case中多个评估值的功能是一样的。

以下是一个初始化表达式结构的switch示例:

func main() {
    val := 21
    switch val := 23; {
    case val % 4 == 0:
        println(0,val)
    case val % 4 == 1 || val % 4 == 2:
        println(1, 2,val)
    default:             // 命中
        println(3,val)   // 输出"3 23"
    }
    println(val)         // 输出21
}

for语句

Go中只有一种循环结构:for。

普通格式的for

// 完整格式的for
for init; condition; modif { }

// 只有条件判断的for,实现while的功能
// 要在循环体中加上退出条件,否则无限循环
for condition { }

例如:

// 完整格式
func main() {
    for i := 0; i < 5; i++ {
        fmt.Println(i)
    }
}

// 只有条件的格式
func main() {
    var i int = 5
    for i >= 0 {
        i = i - 1
        fmt.Printf(i)
    }
}

无限循环

好几种方式实现for的无限循环。只要省略for的条件判断部分就可以实现无限循环。

for i := 0;;i++
for { }
for ;; { }
for true { }

无限循环时,一般在循环体中加上退出语句,如break、os.Exit、return等。

for range遍历

range关键字非常好用,可以用来迭代那些可迭代的对象。比如slice、map、array,还可以迭代字符串,甚至是Unicode的字符串。

for index,value := range XXX {}

但千万注意,value是从XXX中拷贝的副本,所以通过value去修改XXX中的值是无效的,在循环体中应该总是让value作为一个只读变量。如果想要修改XXX中的值,应该通过index索引到源值去修改(不同类型修改的方式不一样)。

以迭代字符串为例。

func main() {
    var a = "Xiaofang,你好"
    for index,value := range a {
        println(index,string(value))
    }
}

输出结果:

0 X
1 i
2 a
3 o
4 f
5 a
6 n
7 g
8 ,
9 你
12 好

可见,在迭代字符串的时候,是按照字符而非字节进行索引的。

下面通过value去修改slice将无效。

func main() {
    s1 := []int{11,22,33}
    for index,value := range s1 {
        value += 1      // 只在for结构中有效
        fmt.Println(index,value)
    }
    fmt.Println(s1)   // for外面的结果仍然是[11 22 33]
}

要在循环结构中修改slice,应该通过index索引的方式:

func main() {
    s1 := []int{11,22,33}
    for index,value := range s1 {
        value += 1
        s1[index] = value
        fmt.Println(index,value)
    }
    fmt.Println(s1)   // [12 23 34]
}

break和continue

breake用于退出当前整个循环。如果是嵌套的循环,则退出它所在的那一层循环。break除了可以用在for循环中,还可以用在switch结构或select结构。

continue用于退出当前迭代,进入下一轮迭代。continue只能用于for循环中。

标签和goto

当某一行中第一个单词后面跟一个冒号的时候,Go就认为这是一个标签。例如:

func main() {
LABEL1:
    for i := 0; i <= 5; i++ {
        for j := 0; j <= 5; j++ {
            if j == 4 {
                continue LABEL1
            }
            fmt.Printf("i is: %d, and j is: %d\n", i, j)
        }
    }
}

使用标签能让break、continue以及goto跳转到指定的位置继续往下执行。例如这里的continue LABEL1,当j == 4的时候,就直接跳到外层循环进入下一轮迭代。而break LABEL则指定直接退出LABEL所在的那一层循环。

goto懒得介绍了,反正没人用,也强烈不建议使用,甚至标签都建议不要使用。一般能使用LABEL或goto的结构,都能改写成其它更好的语句。

原文地址:https://www.cnblogs.com/f-ck-need-u/p/9866091.html

时间: 2024-10-10 06:06:53

Go基础系列:流程控制结构的相关文章

Scrum入门基础系列之Scrum会议

Scrum入门基础系列之Scrum会议 3条回复 Scrum会议包含Sprint计划会.每日例会.Sprint评审会.Sprint回顾会.下面分别介绍这几个会议,按照一个简单模板进行介绍: WHY.WHAT.WHEN.WHO.HOW,即为什么要有这个会议,这个会议的输入和输出是什么,什么时间开这个会,谁来参加,如何开好这个会议. Sprint计划会 WHY Sprint计划会是为当前Sprint做计划的会议. WHAT Sprint计划会的输入为产品Backlog,最新的产品增量,团队的能力和开

Scrum入门基础系列之Scrum起源

Scrum入门基础系列之Scrum起源 5条回复 说起Scrum就不得不提Scrum之父 – Jeff Sutherland和Ken Schwaber,Jeff在1993年结合他的工作实践创建了Scrum框架,1995年Ken在OOPSLA会议上第一次发表Scrum的论文.此后Scrum之父的两位分别撰写过多篇文章,并联合发布了<Scrum Guide>(Scrum指南).有关具体的Scrum起源可以参考我之前的一篇博文 – Scrum起源. 说到Scrum的起源,让我们再来看看Scrum的3

Scrum入门基础系列之Scrum框架

Scrum入门基础系列之Scrum框架 3条回复 读过几本Scrum书的人,想必对于Scrum框架都可以如数家珍,如Scrum的3个角色,5个会议,3个工件.在展开这些内容之前,我想先介绍一下Scrum的价值观以及敏捷宣言. 敏捷宣言[1] 个体与互动    胜于    流程与工具 可工作的软件    胜于    详尽的文档 客户协作    胜于    合同谈判 响应变化    胜于    遵循计划 也就是说,尽管右项有其价值,我们更看重左项. Scrum价值观[2] 专注:一段时间内只专注于少

J2EE开发实战基础系列之开卷有益

时隔七年再次接触培训有关的事情,是兴奋,更多的是恐惧,不知该如何下手. 本系列针对有Java语法基础的开发者或者爱好者,从工作开发角度出发讲解,不同于其他视频,一切皆以实用为主,过程中如有疑问,请提问于我,回答将发布在教程中添加提问部分,提问者越多,教程覆盖越全面,以实际问题为主. ----------------------------------------------------------------------------------------- 首先介绍下目前J2EE方面培训的入门

swift基础之流程控制

?Swift 流程控制简介 ?Swift提供了类似 C 语言的流程控制结构,包括可以多次执行任务的for和while循环, 基于特定条件选择执行不同代码分支的if和switch语句,还有控制流程跳转到其他代码 的break和continue语句. ?除了 C 语言里面传统的for条件递增(for-condition-increment)循环,Swift 还增 加了for-in循环,用来更简单地遍历数组(array),字典(dictionary),区间 (range),字符串(string)和其他

mybatis基础系列(一)&mdash;&mdash;mybatis入门

好久不发博客了,写博文的一个好处是能让心静来,整理下之前学习过的一些知识一起分享,大神路过~ mybatis简介 MyBatis 是一款优秀的持久层框架,它支持定制化 SQL.存储过程以及高级映射.传统的 JDBC代码通常存在如下问题: 1. 频繁对数据库进行连接和关闭,造成资源浪费,性能下降. 2. SQL代码.preparedStatement参数.占位符号等硬编码到代码中,不方便维护. 3. 遍历结果集数据时,硬编码解析表字段成java属性,不方便维护. MyBatis 避免了几乎所有的

EnjoyingSoft之Mule ESB基础系列第二篇:Mule ESB基本概念

目录 1. 使用Anypoint Studio开发 2. Mule ESB Application Structure - Mule ESB应用程序结构 3. Mule ESB Application整体构造 4. Mule ESB构造元素 - Flow 5. Mule ESB构造元素 - Connector 6. Mule ESB构造元素 - Processor Mule ESB在众多开源的ESB中处于领先者的地位,MuleSoft公司也作为独角兽,2017年在纽交所上市.我们作为MuleSo

夯实Java基础系列10:深入理解Java中的异常体系

目录 为什么要使用异常 异常基本定义 异常体系 初识异常 异常和错误 异常的处理方式 "不负责任"的throws 纠结的finally throw : JRE也使用的关键字 异常调用链 自定义异常 异常的注意事项 当finally遇上return JAVA异常常见面试题 参考文章 微信公众号 Java技术江湖 个人公众号:黄小斜 - Java异常 本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https://github.c

java基础-控制流程语句

一 前言 周末睡觉好舒服,都不想动了,就想睡睡,晒晒太阳,作者劳碌命还是过来写文章了.基础系列文章已经已经出到控制流程,感觉也挺快的,我很自信全网没都多少系列文章能有我这基础系列写的这么好,易于初学者理解并且知识体系齐全觉得文章不错的可以关注一下作者.控制流程语句的概念其实作者感觉是从工程流程图里面转换过来的,比如是非选择啊,循环执行啊之类都可以用流程图来讲述咯!!!!!! 二 if - else 语句 2.1 if -else if (如果)- else(否则) 在之前的基础系列三元运算符中有

C#基础系列:实现自己的ORM(反射以及Attribute在ORM中的应用)

反射以及Attribute在ORM中的应用 一. 反射什么是反射?简单点吧,反射就是在运行时动态获取对象信息的方法,比如运行时知道对象有哪些属性,方法,委托等等等等.反射有什么用呢?反射不但让你在运行是获取对象的信息,还提供运行时动态调用对象方法以及动态设置.获取属性等的能力.反射在ORM中有什么用呢?我这里所讨论的ORM实现是通过自定义Attribute的方式进行映射规则的描述的.但是我们并不知道具体哪个对象需要对应哪个表,并且这些对象是独立于我们的ORM框架的,所以我们只能通过自定义Attr