首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Golang 学习笔记-2:控制流

Golang 学习笔记-2:控制流

作者头像
goodspeed
发布于 2020-12-22 01:53:18
发布于 2020-12-22 01:53:18
40900
代码可运行
举报
文章被收录于专栏:厉害了程序员厉害了程序员
运行总次数:0
代码可运行

上一篇我们了解了golang 的变量、函数和基本类型,这一篇将介绍一下控制流

现在我们看一个复杂点的例子:

fibonacci(递归版)

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package main
import "fmt"

func main() {
    result := 0
    for i := 0; i <= 10; i++ {
         result = fibonacci(i)
         fmt.Printf("fibonacci(%d) is: %d\n", i, result)
      }
}

func fibonacci(n int) (res int) {
    if n <= 1 {
        res = 1
       } else {
           res = fibonacci(n-1) + fibonacci(n-2)
       }
    return
}

    // outputs

    fibonacci(0) is: 1
    fibonacci(1) is: 1
    fibonacci(2) is: 2
    fibonacci(3) is: 3
    fibonacci(4) is: 5
    fibonacci(5) is: 8
    fibonacci(6) is: 13
    fibonacci(7) is: 21
    fibonacci(8) is: 34
    fibonacci(9) is: 55
    fibonacci(10) is: 89
  • for i := 0; i <= 10; i++ {} 第7行是一个循环结构 这里for 循环是一个控制流

控制流

For

Go 只有一种循环接口-- for 循环

For 支持三种循环方式,包括类 while 语法

1 基本for循环 支持初始化语句
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
s := "abc"
for i, n := 0, len(s); i < n; i++ {
    // i, n 为定义的变量 只在for 循环内作用
    println(s[i])
}

基本的 for 循环包含三个由分号分开的组成部分:

  • 初始化语句:在第一次循环执行前被执行
  • 循环条件表达式:每轮迭代开始前被求值
  • 后置语句:每轮迭代后被执行
2 替代 while (n > 0) {}

C 的 while 在 Go 中叫做 for

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
n := len(s)
// 循环初始化语句和后置语句都是可选的。
for n > 0 { // 等同于 for (; n > 0;) {}
    println(s[n])
    n--
}
3 死循环
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    for { // while true
        println(s)
    }

IF…ELSE

就像 for 循环一样,Go 的 if 语句也不要求用 ( ) 将条件括起来,同时, { } 还是必须有的

  • 条件表达式必须是布尔类型,可省略条件表达式括号
  • 支持初始化语句,可定义代码块局部变量
  • 代码块左大括号必须在条件表达式尾部
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
x := 0

// if x > 10  // Error: missing condition in if statement(左大括号必须在条件表达式尾部)
// {
// }

if x > 10{
    ...
}else{
    ...
}

if n := "abc"; x > 0 {  // 初始化语句(在这里是定义变量)
    println(n[2])
} else if x < 0 {
    println(n[1])
} else {
    println(n[0])     // 局部变量 n 有效范围是 整个 if/else 块
}

if 语句定义的变量作用域仅在if范围之内(包含else语句)不支持三元操作符 "a > b ? a : b"

以上是上段代码出现的两个控制流,剩下的控制流还有

  • Switch
  • Range
  • Goto, Break, Continue, defer

Switch

switch 语句用于选择执行,语法如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    switch optionalStatement; optionalExpression{
        case expressionList1: block1
        ...
        case expressionListN: blockN
        default: blockD
    }

先看一个例子:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package main

import (
    "fmt"
    "runtime"
)

func main() {
    fmt.Print("Go runs on ")
    switch os := runtime.GOOS; os { // 将 os 与 case 条件匹配
    case "darwin":
        fmt.Println("OS X.")
    case "linux":
        fmt.Println("Linux.")
    case "plan9", "openbsd": // 多个条件命中其一即可(OR)
        fmt.Println("plan9 | openbsd")
    default:
        // freebsd, openbsd,
        // plan9, windows...
        fmt.Printf("%s.", os)
    }
}
  • 如果有可选语句声明, 分号是必要的, 无论后边的可选表达式语句是否出现(如果可选语句没有出现默认为true)
  • 每一个case 语句必须要有一个表达式列表,多个用分号隔开
  • switch 语句自上而下执行,当匹配成功后执行case分支的代码块,执行结束后退出switch
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
switch i {
case 0: // 空分支,只有当 i == 0 时才会进入分支 相当于 "case 0: break;"
case 1:
    f() // 当 i == 0 时函数不会被调用
}
  • 如果想要在执行完每个分支的代码后还继续执行后续的分支代码,可以使用fallthrough 关键字达到目的
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package main

import "fmt"

func switch1(n int) {
    switch { // 这里用的是没有条件的switch 语句会直接执行
    case n == 0:
        fmt.Println(0)
        fallthrough  // fallthrough 需放在 case 块结尾,可用 break 阻止
    case n == 1: // 如果匹配到0 这里会继续执行
        fmt.Println(1)
    case n == 2: // fallthrough 不会对这里有作用
        fmt.Println(2)
    default:
        fmt.Println("default")
    }
}

func main() {
    switch1(0)
}
# output
0
1
  • 用 default 可以指定当其他所有分支都不匹配的时候的行为
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    switch i {
        case 0:
        case 1:
            f()
        default:
            g()  // 当i不等于0 或 1 时调用
    }

Range

Range 类似迭代器的操作,返回(索引,值)或(健,值)

它可以迭代任何一个集合(包括字符串、数组、数组指针、切片、字典、通道)

基本语法如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
coll := 3string["a", "b", "c"]
for ix, val := range coll {
   ...
}

// 允许返回单值

for ix := range coll {
    println(ix, coll[ix])
}

// 也可以使用 _ 忽略

for _, val := range coll {
    println(val)
}

// 也可以只迭代,不返回。可用来执行清空 channel 等操作

for range coll {
    ...
}

val 始终为集合中对应索引的值拷贝,因此它一般只具有只读性质,对它所做的任何修改都不会影响到集合中原有的值(译者注:如果 val 为指针,则会产生指针的拷贝,依旧可以修改集合中的原值一个字符串是 Unicode 编码的字符(或称之为 rune)集合,因此您也可以用它迭代字符串

下面是每种数据类型使用range时 ix和val 的值

date type

ix

value

值类型

string

index

s[index]

unicode, rune

array/slice

index

s[index]

map

key

m[index]

channel

element

range 会复制目标数据。字符串、切片基本结构是个很小的结构体,而字典、通道本身是指针封装,复制成本很小,无需专门优化。 如果是数组,可改成数组指针或者切片类型。

Break continue

break 和 continue 都可在多级嵌套循环中跳出

break 可用于 for、switch、select语句,终止整个语句块执行 continue 仅能 于 for 循环,终止后续操作,立即进入下一轮循环。

goto

goto 语句可以配合标签(label)形式的标识符使用,即某一行第一个以冒号:结尾的单词,标签区分大小写。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package main

func main() {
    i:=0
    HERE:
        print(i)
        i++
        if i==5 {
            return
        }
        goto HERE
}
# output 01234

使用标签和 goto 语句是不被鼓励的:它们会很快导致非常糟糕的程序设计,而且总有更加可读的替代方案来实现相同的需求。

for、switch 或 select 语句都可以配合标签(label)形式的标识符使用

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package main

import "fmt"

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)
        }
    }
}

continue 语句指向 LABEL1,当执行到该语句的时候,就会跳转到 LABEL1 标签的位置

defer

defer 语句会延迟函数的执行直到上层函数返回

延迟调用的参数会立刻生成,但是在上层函数返回前函数都不会被调用

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package main

import "fmt"

func main() {
    defer fmt.Println("world")

    fmt.Println("hello")
}

// output
hello
world

defer 栈

延迟的函数调用被压入一个栈中。当函数返回时, 会按照后进先出的顺序调用被延迟的函数调用。defer 常用来定义简单的方法

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package main

import "fmt"

func main() {
    fmt.Println("counting")

    for i := 0; i < 10; i++ {
            defer fmt.Println(i)
        }

        fmt.Println("done")
}
// 可以想一下会输出什么
// 代码执行 https://tour.go-zh.org/flowcontrol/13

关键字 defer 允许我们进行一些函数执行完成后的收尾工作,例如:

  • 关闭文件流:
  • 解锁一个加锁的资源 mu.Lock() defer mu.Unlock()
  • 打印最终报告 printHeader() defer printFooter()
  • 关闭数据库链接

合理使用 defer 语句能够使得代码更加简洁。

下面的代码展示了在调试时使用 defer 语句的手法

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package main

import (
    "io"
    "log"
)

func func1(s string) (n int, err error) {
    defer func() {
            log.Printf("func1(%q) = %d, %v", s, n, err)
        }()
    return 7, io.EOF
}

func main() {
    func1("Go")
}

// 输出
Output: 2016/04/25 10:46:11 func1("Go") = 7, EOF

更多defer 的用法(https://blog.go-zh.org/defer-panic-and-recover)

参考链接

Go 指南The way to go -- 控制结构Effective Go

到这里简单的控制流用法讲解就结束了

下节将会是golang 数据结构部分, 会用到的代码为

fibonacci(内存版)

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 package main

 import (
     "fmt"
     "time"
 )

 const LIM = 41

 var fibs [LIM]uint64

 func main() {
     var result uint64 = 0
     start := time.Now()
     for i := 0; i < LIM; i++ {
         result = fibonacci(i)
         fmt.Printf("fibonacci(%d) is: %d\n", i, result)
     }
     end := time.Now()
     delta := end.Sub(start)
     fmt.Printf("longCalculation took this amount of time: %s\n", delta)
 }
 func fibonacci(n int) (res uint64) {
     // memoization: check if fibonacci(n) is already known in array:
     if fibs[n] != 0 {
         res = fibs[n]
         return
     }
     if n <= 1 {
         res = 1
     } else {
         res = fibonacci(n-1) + fibonacci(n-2)
     }
     fibs[n] = res
     return
 }

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2018-04-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 四月 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
【C语言】基本语法知识&&C语言函数&&操作符详解
首先介绍一下我使用的工具:VS2019——集成了很多的功能:编辑、编译、链接、运行、调试等
用户10925563
2024/06/04
3690
【C语言】基本语法知识&&C语言函数&&操作符详解
【初级】C语言——详解操作符
                浮点型的除法 1.0/2      1/2.0--->0.5
xxxflower
2023/04/16
6120
【初级】C语言——详解操作符
C语言操作符
除了%操作符,其余操作符既适用于浮点类型,又适用于整数类型。当/操作符的两个操作数都是整数时执行整除运算,其他情况执行浮点数除法。 % 为取模操作符,其两个操作数必须为整数,而返回的值是整除之后的余数。
海盗船长
2020/08/27
1.2K0
C语言——操作符详解
前言:这篇文章主要讲解一下C语言中常见的操作符的使用,做一下整理,便于日后回顾,同时也希望能帮助到大家。
用户11029137
2024/03/19
4810
C语言——操作符详解
C语言(7)----操作符
C语言中的操作符是用来进行各种数学运算、逻辑运算、位运算、赋值等操作的符号,那么看着是不是就和我们日常生活中的这些语法结构很像呢?
Skrrapper
2024/06/18
1640
C语言(7)----操作符
初识C语言
自己整理的C语言基础学习笔记,参考了挺多资料的,适合梳理知识框架,进而攻克各个知识点。
小孙同学
2022/01/14
4120
初识C语言
【c语言】运算符汇总(万字解析)
为了促进大家深入理解C语言并提升学习效率,本博客作者将对C语言中常用运算符的功能及其使用方法进行全面梳理,同时整合归纳这些运算符的优先级、结合性以及表达式求值规则。
ephemerals__
2024/10/30
1.2K0
【c语言】运算符汇总(万字解析)
【C语言初阶】C语言操作符全攻略:提升编程效率的关键步骤
前言:在编程的世界里,C语言如同一座巍峨的基石,奠定了无数编程语言与软件系统的基础。作为一门历史悠久且功能强大的编程语言,C语言以其高效、灵活和可移植性赢得了广泛的赞誉和应用。而在C语言的众多组成部分中,操作符无疑是其中最为基础且关键的一环
Eternity._
2024/08/09
2010
【C语言初阶】C语言操作符全攻略:提升编程效率的关键步骤
JAVA逻辑运算符
整型、实型和字符型数据进行混合运算时,须先转换成相同类型。转换从低级到高级: 低-> byte,short,char,int,long,float,double -> 高
默默的成长
2022/11/02
6620
JAVA逻辑运算符
C语言---操作符详解
• 赋值操作符: = 、+= 、 -= 、 *= 、 /= 、%= 、>= 、&= 、|= 、^=
Undoom
2024/09/23
1170
按位与、按位异或、按位取反「建议收藏」
& 按位与 | 按位或 ^ 按位异或 1. 按位与运算 按位与运算符”&”是双目运算符。其功能是参与运算的两数各对应的二进位相与。只有对应的两个二进位均为1时,结果位才为1 ,否则为0。参与运算的数以补码方式出现。 例如:9&5可写算式如下: 00001001 (9的二进制补码)&00000101 (5的二进制补码) 00000001 (1的二进制补码)可见9&5=1。 按位与运算通常用来对某些位清0或保留某些位。例如把a 的高八位清 0 , 保留低八位, 可作 a&255 运算 ( 255 的二进制数为0000000011111111)。 main(){ int a=9,b=5,c; c=a&b; printf(“a=%d/nb=%d/nc=%d/n”,a,b,c); } 2. 按位或运算 按位或运算符“|”是双目运算符。其功能是参与运算的两数各对应的二进位相或。只要对应的二个二进位有一个为1时,结果位就为1。参与运算的两个数均以补码出现。 例如:9|5可写算式如下: 00001001|00000101 00001101 (十进制为13)可见9|5=13 main(){ int a=9,b=5,c; c=a|b; printf(“a=%d/nb=%d/nc=%d/n”,a,b,c); } 3. 按位异或运算 按位异或运算符“^”是双目运算符。其功能是参与运算的两数各对应的二进位相异或,当两对应的二进位相异时,结果为1。参与运算数仍以补码出现,例如9^5可写成算式如下: 00001001^00000101 00001100 (十进制为12) main(){ int a=9; a=a^15; printf(“a=%d/n”,a); }
全栈程序员站长
2022/09/06
2.5K0
按位与、或、异或等运算方法
运算规则:0&0=0;   0&1=0;    1&0=0;     1&1=1;
Java架构师必看
2021/03/22
2.4K0
【C语言】操作符还能这样?
✨作者:@平凡的人1 ✨专栏:《C语言从0到1》 ✨一句话:凡是过往,皆为序章 ✨说明: 过去无可挽回, 未来可以改变 ---- 文章目录 @[toc] 🎬前言 🚀操作符的分类 🚀算术操作符 🚀移位操作符 🚩左移操作符 🚩右移操作符 🚀位操作符 🚩**不创建临时变量交换两个整数** 🚩求一个整数存储在内存中的二进制中1的个数 🚀赋值操作符 🚀复合赋值符 🚀单目操作符 🚀关系操作符 🚀逻辑操作符 🚀条件操作符 🚀逗号表达式 🚀下标引用、函数调用和结构成员 🚀拓展:表达式求值 🚩隐式类型转换
平凡的人1
2022/11/15
8390
【C语言】操作符还能这样?
C \u002F C++ 中的运算符
theme: channing-cyan highlight: a11y-dark
鲸落c
2022/11/14
6440
Java的位运算符详解实例——与(&)、非(~)、或(|)、异或(^)
      位运算符主要针对二进制,它包括了:“与”、“非”、“或”、“异或”。从表面上看似乎有点像逻辑运算符,但逻辑运算符是针对两个关系运算符来进行逻辑运算,而位运算符主要针对两个二进制数的位进行逻辑运算。下面详细介绍每个位运算符。
allsmallpig
2021/02/25
1.4K0
C语言位运算符
对于更多紧凑的数据,C 程序可以用独立的位或多个组合在一起的位来存储信息。文件访问许可就是一个常见的应用案例。位运算符允许对一个字节或更大的数据单位中独立的位做处理:可以清除、设定,或者倒置任何位或多个位。也可以将一个整数的位模式(bit pattern)向右或向左移动。 整数类型的位模式由一队按位置从右到左编号的位组成,位置编号从 0 开始,这是最低有效位(least significant bit)。例如,考虑字符值'*',它的 ASCII 编码为 42,相当于二进制的 101010: 位模式 0 0 1 0 1 0 1 0 位位置 7 6 5 4 3 2 1 0 在本例中,值 101010 被表示成一个 8 位的字节内容,因此前面多两个 0。
用户6755376
2020/03/19
2.1K0
C语言——H/操作符详解
赋值操作符: = 、+= 、 -= 、 *= 、 /= 、%= 、<<= 、>>= 、&= 、|= 、^=
用户11015888
2024/03/11
5830
C语言——H/操作符详解
【C语言】操作符
其实我们经常能听到2进制、8进制、10进制、16进制这样的讲法,那是什么意思呢?其实2进制、8进制、10进制、16进制是数值的不同表示形式而已。 比如:数值15的各种进制的表示形式:
zxctscl
2024/09/30
1790
【C语言】操作符
【C语言篇】操作符详解(下篇)
​ 如果表达式1为真,那么就计算表达式2,表达式2的结果为整个式子的 结果; ​ 如果表达式1为假,那么就计算表达式3,表达式3的结果为整个式子的结果。
半截诗
2024/10/09
1110
【C语言篇】操作符详解(下篇)
关于我、重生到500年前凭借C语言改变世界科技vlog.10——进制转化&&操作符进阶
操作符在写代码中有很大的作用,是用于执行特定操作的符号,主要在算术运算、比较运算、逻辑运算、位运算(用于二进制数据处理)起作用,C语言开篇已经介绍了一部分,接下来将进行一些进阶的介绍
DARLING Zero two
2024/11/19
1060
关于我、重生到500年前凭借C语言改变世界科技vlog.10——进制转化&&操作符进阶
相关推荐
【C语言】基本语法知识&&C语言函数&&操作符详解
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档