条件判断结构:if else

分支选择结构:switch case

循环结构:for

break:退出for或switch结构(以及select)

continue:进入下一次for迭代

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

如:

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

if语句

  1. if condition1 {
  2. // do something
  3. } else if condition2 {
  4. // do something else
  5. } else {
  6. // catch-all or default
  7. }

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

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

  1. if val := 10; val > max {
  2. // do something
  3. }

它在一定程度上等价于:

  1. val := 10
  2. if val > max {
  3. // do something
  4. }

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

  1. func main() {
  2. val := 20
  3. if val := 10; val > 3 {
  4. println("true")
  5. }
  6. println(val) // 输出20
  7. }

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

  1. func main() {
  2. val := 20
  3. if val = 10; val > 3 {
  4. println("true")
  5. }
  6. println(val) // 输出10
  7. }

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

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

  1. value,ok := func_name()
  2. if !ok {
  3. // func_name执行错误
  4. os.Exit(1)
  5. }
  6. value,err := func_name()
  7. if err != nil {
  8. // func_name执行错误
  9. os.Exit(1)
  10. // 或 return err
  11. }

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

  1. if value,ok := func_name();ok {
  2. // ok为true,函数执行成功
  3. } else {
  4. // ok为false,函数执行失败
  5. os.Exit(1)
  6. }
  7. if value,err := func_name();err != nil {
  8. // err不为nil,说明出现错误
  9. return err
  10. //或os.Exit(1)
  11. } else {
  12. // err为空,说明执行正确
  13. }

switch语句

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

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

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

  1. switch var1 {
  2. case val1:
  3. statement1
  4. case val2:
  5. statement2
  6. default:
  7. statement
  8. }

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

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

  1. switch {
  2. case condition1:
  3. statement1
  4. case condition2:
  5. statement2
  6. default:
  7. statement
  8. }

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

  1. switch initialization; { // 不要省略分号
  2. case condition1:
  3. statement1
  4. case condition2:
  5. statement2
  6. defautl:
  7. statement
  8. }

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

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

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

  1. switch var1 {
  2. case val1,val2,val3:
  3. statement1
  4. case val4,val5:
  5. statement2
  6. default:
  7. statement
  8. }

例如:

  1. val := 20
  2. switch val {
  3. case 10, 11, 15:
  4. println(11, 15)
  5. case 16, 20, 22: // 命中
  6. println(16, 20, 22)
  7. default:
  8. println("nothing")
  9. }

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

  1. func main() {
  2. val := 21
  3. switch {
  4. case val % 4 == 0:
  5. println(0)
  6. case val % 4 == 1, val % 4 == 2: //命中
  7. println(1, 2)
  8. default:
  9. println("3")
  10. }
  11. }

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

例如:

  1. func main() {
  2. val := 21
  3. switch val % 4 {
  4. case 0:
  5. println(0)
  6. case 1, 2: // 命中
  7. println(1, 2) // 输出
  8. fallthrough // 执行下一条,无需条件评估
  9. // println("sd") //不能加此行语句
  10. case 3:
  11. println(3) // 输出
  12. fallthrough // 执行下一条,无需条件评估
  13. default:
  14. println("end") // 输出
  15. }
  16. }

执行结果为:

  1. 1 2
  2. 3
  3. end

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

  1. swtich i {
  2. case 0: fallthrough
  3. case 1: statement1
  4. default: statement
  5. }

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

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

  1. func main() {
  2. val := 21
  3. switch val := 23; {
  4. case val % 4 == 0:
  5. println(0,val)
  6. case val % 4 == 1 || val % 4 == 2:
  7. println(1, 2,val)
  8. default: // 命中
  9. println(3,val) // 输出"3 23"
  10. }
  11. println(val) // 输出21
  12. }

for语句

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

普通格式的for

  1. // 完整格式的for
  2. for init; condition; modif { }
  3. // 只有条件判断的for,实现while的功能
  4. // 要在循环体中加上退出条件,否则无限循环
  5. for condition { }

例如:

  1. // 完整格式
  2. func main() {
  3. for i := 0; i < 5; i++ {
  4. fmt.Println(i)
  5. }
  6. }
  7. // 只有条件的格式
  8. func main() {
  9. var i int = 5
  10. for i >= 0 {
  11. i = i - 1
  12. fmt.Printf(i)
  13. }
  14. }

无限循环

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

  1. for i := 0;;i++
  2. for { }
  3. for ;; { }
  4. for true { }

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

for range遍历

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

  1. for index,value := range XXX {}

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

以迭代字符串为例。

  1. func main() {
  2. var a = "Xiaofang,你好"
  3. for index,value := range a {
  4. println(index,string(value))
  5. }
  6. }

输出结果:

  1. 0 X
  2. 1 i
  3. 2 a
  4. 3 o
  5. 4 f
  6. 5 a
  7. 6 n
  8. 7 g
  9. 8 ,
  10. 9
  11. 12

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

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

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

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

  1. func main() {
  2. s1 := []int{11,22,33}
  3. for index,value := range s1 {
  4. value += 1
  5. s1[index] = value
  6. fmt.Println(index,value)
  7. }
  8. fmt.Println(s1) // [12 23 34]
  9. }

break和continue

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

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

标签和goto

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

  1. func main() {
  2. LABEL1:
  3. for i := 0; i <= 5; i++ {
  4. for j := 0; j <= 5; j++ {
  5. if j == 4 {
  6. continue LABEL1
  7. }
  8. fmt.Printf("i is: %d, and j is: %d\n", i, j)
  9. }
  10. }
  11. }

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

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

空语句块

Go中支持空block{},这个大括号有自己的作用域,里面的代码只执行一次,退出大括号就退出作用域。

  1. func main() {
  2. {
  3. v := 1
  4. {
  5. v := 2
  6. fmt.Println(v) // 输出2
  7. }
  8. fmt.Println(v) // 输出1
  9. }
  10. }

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

  1. java基础(5)--流程控制结构

    流程控制结构 if结构 当关系表达式为true时,执行语句 if(关系表达式){ //语句块 } if-else结构 当关系表达式为true时,执行语句块1,否则执行语句块2 if(关系表达式){ / ...

  2. Mysql基础(十一):流程控制结构、分支结构、循环结构

    流程控制结构 说明:顺序结构:程序从上往下依次执行分支结构:程序按条件进行选择执行,从两条或多条路径中选择一条执行循环结构:程序满足一定条件下,重复执行一组语句 分支结构 特点:1.if函数功能:实现 ...

  3. C#基础系列——委托和设计模式(二)

    前言:前篇 C#基础系列——委托实现简单设计模式 简单介绍了下委托的定义及简单用法.这篇打算从设计模式的角度去解析下委托的使用.我们知道使用委托可以实现对象行为(方法)的动态绑定,从而提高设计的灵活性 ...

  4. JavaScript基础系列

    JavaScript基础系列 JavaScript是一种基于对象和事件驱动的客户端脚本语言. JavaScript的注释 // 单行 /**/ 多行注释 JavaScript变量,函数名和操作符都是区 ...

  5. JVM基础系列第7讲:JVM 类加载机制

    当 Java 虚拟机将 Java 源码编译为字节码之后,虚拟机便可以将字节码读取进内存,从而进行解析.运行等整个过程,这个过程我们叫:Java 虚拟机的类加载机制.JVM 虚拟机执行 class 字节 ...

  6. mybatis基础系列(四)——关联查询、延迟加载、一级缓存与二级缓存

    关本文是Mybatis基础系列的第四篇文章,点击下面链接可以查看前面的文章: mybatis基础系列(三)——动态sql mybatis基础系列(二)——基础语法.别名.输入映射.输出映射 mybat ...

  7. mybatis基础系列(一)——mybatis入门

    好久不发博客了,写博文的一个好处是能让心静下来,整理下之前学习过的一些知识一起分享,大神路过~ mybatis简介 MyBatis 是一款优秀的持久层框架,它支持定制化 SQL.存储过程以及高级映射. ...

  8. 基础系列(5)—— C#控制语句

    语句是程序中最小程序指令.C#语言中可以使用多种类型的语句,每一种类型的语句又可以通过多个关键字实现.以下是C# 语言中使用的主要控制语句 类别 关键字 选择语句  if.else.switch.ca ...

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

    2014.10.24[致歉]{抱歉,从7.4号接到朋友的请求,一直忙到现在,最近又有新的CASE要忙,很抱歉教程要延误,开课时间请大家关注Q群} 时隔七年再次接触培训有关的事情,是兴奋,更多的是恐惧, ...

  10. linux驱动基础系列--linux spi驱动框架分析

    前言 主要是想对Linux 下spi驱动框架有一个整体的把控,因此会忽略某些细节,同时里面涉及到的一些驱动基础,比如平台驱动.设备模型等也不进行详细说明原理.如果有任何错误地方,请指出,谢谢! spi ...

随机推荐

  1. 模板层template

    继续之前的views,你可 能已经注意到我们例子中视图中返回的的方式有点特别.也就是说.HTML被直接硬编码在Python代码之中 def current_datetime(request): now ...

  2. Exp2后门原理与实践_20154305 _ 齐 帅

    Exp2后门原理与实践 20154305 _ 齐 帅 2.1简单后门 一.后门工具介绍 1.netcat(nc.ncat) 是一个底层工具,进行基本的TCP UDP数据收发.常被与其他工具结合使用,起 ...

  3. (转)pycharm常用快捷键

    Alt+Enter 自动添加包Ctrl+t SVN更新Ctrl+k SVN提交Ctrl + / 注释(取消注释)选择的行Ctrl+Shift+F 高级查找Ctrl+Enter 补全Shift + En ...

  4. search

    |—search()—|—添加一个列表变量Expend,存储每个小格扩展时为第几步,可打印出 |                    |—打印运动表 |—A*—|— heuristic() |—Dy ...

  5. Leetcode(一)两数之和

    1.两数之和 题目要求: 给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个整数,并返回他们的数组下标. 你可以假设每种输入只会对应一个答案.但是,你不能重 ...

  6. Java 8 特性

    1.简介 毫无疑问,Java 8是自Java  5(2004年)发布以来Java语言最大的一次版本升级,Java 8带来了很多的新特性,比如编译器.类库.开发工具和JVM(Java虚拟机).在这篇教程 ...

  7. DevOps最佳工具集实践

    在列出DevOps 工具链之前,介绍一下什么是DevOps,虽然DevOps这个概念现在还没有标准的定义,但我们可以追溯一下其过去九年的历史发展过程(从2009年-2017年),列出几个相对明确又有所 ...

  8. Maven整合SSM测试

    前面也说到了关于SSM的整合,话不多说直接从创建项目开始CRUD之路(参考前面写过的Mybatis和Spring整合,SSM简单整合),这是整个项目的结构 以及最终的结果.(附上下载地址) 一.创建M ...

  9. 阿里,百度面试90%会问的Java面试题

    题目一 请对比 Exception 和 Error,另外,运行时异常与一般异常有什么区别? 考点分析: 分析 Exception 和 Error 的区别,是从概念角度考察了 Java 处理机制.总的来 ...

  10. Java核心技术卷一基础知识-第8章-事件处理-读书笔记

    第8章 事件处理 本章内容: * 事件处理基础 * 动作 * 鼠标事件 * AWT事件继承层次 8.1 事件处理基础 在AWT所知的事件范围内,完全可以控制事件从事件源(event source)例如 ...