热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

?年学go2:控制流

上一篇我们了解了golang的变量、函

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

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

fibonacci(递归版)

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循环 支持初始化语句

s := "abc"
for i, n := 0, len(s); i // i, n 为定义的变量 只在for 循环内作用
println(s[i])
}

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

  • 初始化语句:在第一次循环执行前被执行

  • 循环条件表达式:每轮迭代开始前被求值

  • 后置语句:每轮迭代后被执行

2 替代 while (n > 0) {}

C 的 while 在 Go 中叫做 for

n := len(s)
// 循环初始化语句和后置语句都是可选的。
for n > 0 { // 等同于 for (; n > 0;) {}
println(s[n])
n--
}

3 死循环

for { // while true
println(s)
}

IF…ELSE


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

  • 条件表达式必须是布尔类型,可省略条件表达式括号

  • 支持初始化语句,可定义代码块局部变量

  • 代码块左大括号必须在条件表达式尾部

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 语句用于选择执行,语法如下:

switch optionalStatement; optionalExpression{
case expressionList1: block1
...
case expressionListN: blockN
default: blockD
}

先看一个例子:

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

switch i {
case 0: // 空分支,只有当 i == 0 时才会进入分支 相当于 "case 0: break;"
case 1:
f() // 当 i == 0 时函数不会被调用
}

  • 如果想要在执行完每个分支的代码后还继续执行后续的分支代码,可以使用fallthrough 关键字达到目的

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 可以指定当其他所有分支都不匹配的时候的行为

switch i {
case 0:
case 1:
f()
default:
g() // 当i不等于0 或 1 时调用
}

Range


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

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

基本语法如下:

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 typeixvalue值类型
stringindexs[index]unicode, rune
array/sliceindexs[index]
mapkeym[index]
channelelement
range 会复制目标数据。字符串、切片基本结构是个很小的结构体,而字典、通道本身是指针封装,复制成本很小,无需专门优化。

如果是数组,可改成数组指针或者切片类型。


Break continue

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

break 可用于 for、switch、select语句,终止整个语句块执行

continue 仅能 于 for 循环,终止后续操作,立即进入下一轮循环。


goto

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

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

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

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

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 语句会延迟函数的执行直到上层函数返回

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

package main
import "fmt"
func main() {
defer fmt.Println("world")
fmt.Println("hello")
}
// output
hello
world

defer 栈

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

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 允许我们进行一些函数执行完成后的收尾工作,例如:

  • 关闭文件流:

    // open a file defer file.Close()


  • 解锁一个加锁的资源

    mu.Lock() defer mu.Unlock()


  • 打印最终报告

    printHeader() defer printFooter()


  • 关闭数据库链接

    // open a database connection defer disconnectFromDB()


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

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

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(内存版)

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


最后,感谢女朋友支持和包容,比❤️

想了解以下内容可以在公号输入相应关键字获取历史文章: 公号&小程序 | 设计模式 | 并发&协程


推荐阅读
  • Go Cobra命令行工具入门教程
    本文介绍了Go语言实现的命令行工具Cobra的基本概念、安装方法和入门实践。Cobra被广泛应用于各种项目中,如Kubernetes、Hugo和Github CLI等。通过使用Cobra,我们可以快速创建命令行工具,适用于写测试脚本和各种服务的Admin CLI。文章还通过一个简单的demo演示了Cobra的使用方法。 ... [详细]
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • 本文介绍了一个Java猜拳小游戏的代码,通过使用Scanner类获取用户输入的拳的数字,并随机生成计算机的拳,然后判断胜负。该游戏可以选择剪刀、石头、布三种拳,通过比较两者的拳来决定胜负。 ... [详细]
  • 本文讨论了如何优化解决hdu 1003 java题目的动态规划方法,通过分析加法规则和最大和的性质,提出了一种优化的思路。具体方法是,当从1加到n为负时,即sum(1,n)sum(n,s),可以继续加法计算。同时,还考虑了两种特殊情况:都是负数的情况和有0的情况。最后,通过使用Scanner类来获取输入数据。 ... [详细]
  • 本文介绍了如何在给定的有序字符序列中插入新字符,并保持序列的有序性。通过示例代码演示了插入过程,以及插入后的字符序列。 ... [详细]
  • Python正则表达式学习记录及常用方法
    本文记录了学习Python正则表达式的过程,介绍了re模块的常用方法re.search,并解释了rawstring的作用。正则表达式是一种方便检查字符串匹配模式的工具,通过本文的学习可以掌握Python中使用正则表达式的基本方法。 ... [详细]
  • Java学习笔记之面向对象编程(OOP)
    本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
  • 开发笔记:实验7的文件读写操作
    本文介绍了使用C++的ofstream和ifstream类进行文件读写操作的方法,包括创建文件、写入文件和读取文件的过程。同时还介绍了如何判断文件是否成功打开和关闭文件的方法。通过本文的学习,读者可以了解如何在C++中进行文件读写操作。 ... [详细]
  • 本文讨论了使用差分约束系统求解House Man跳跃问题的思路与方法。给定一组不同高度,要求从最低点跳跃到最高点,每次跳跃的距离不超过D,并且不能改变给定的顺序。通过建立差分约束系统,将问题转化为图的建立和查询距离的问题。文章详细介绍了建立约束条件的方法,并使用SPFA算法判环并输出结果。同时还讨论了建边方向和跳跃顺序的关系。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • sklearn数据集库中的常用数据集类型介绍
    本文介绍了sklearn数据集库中常用的数据集类型,包括玩具数据集和样本生成器。其中详细介绍了波士顿房价数据集,包含了波士顿506处房屋的13种不同特征以及房屋价格,适用于回归任务。 ... [详细]
  • 关键词:Golang, Cookie, 跟踪位置, net/http/cookiejar, package main, golang.org/x/net/publicsuffix, io/ioutil, log, net/http, net/http/cookiejar ... [详细]
  • [大整数乘法] java代码实现
    本文介绍了使用java代码实现大整数乘法的过程,同时也涉及到大整数加法和大整数减法的计算方法。通过分治算法来提高计算效率,并对算法的时间复杂度进行了研究。详细代码实现请参考文章链接。 ... [详细]
  • ALTERTABLE通过更改、添加、除去列和约束,或者通过启用或禁用约束和触发器来更改表的定义。语法ALTERTABLEtable{[ALTERCOLUMNcolu ... [详细]
  • Spring学习(4):Spring管理对象之间的关联关系
    本文是关于Spring学习的第四篇文章,讲述了Spring框架中管理对象之间的关联关系。文章介绍了MessageService类和MessagePrinter类的实现,并解释了它们之间的关联关系。通过学习本文,读者可以了解Spring框架中对象之间的关联关系的概念和实现方式。 ... [详细]
author-avatar
十只北羊鱼
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有