Go语言编程 学习笔记整理 第2章 顺序编程 后半部分

1.流程控制

1.1 条件语句

if a < 5 { return 0 
} else { return 1 
}

注意:在有返回值的函数中,不允许将“最终的”return语句包含在if...else...结构中, 否则会编译失败!!!

func example(x int) int { if x == 0 { return 5 } else { return x } 
}// 编译失败的原因:Go编译器无法找到终止该函数的return语句

1.2 选择语句

switch i { case 0: fmt.Printf("0") case 1: fmt.Printf("1") case 2: fallthrough case 3: fmt.Printf("3") case 4, 5, 6: fmt.Printf("4, 5, 6") default: fmt.Printf("Default") 
}
运行上面的案例,将会得到如下结果:
i = 0 时,输出 0
i = 1 时,输出 1
i = 2 时,输出 3
i = 3 时,输出 3
i = 4 时,输出 4, 5, 6
i = 5 时,输出 4, 5, 6
i = 6 时,输出 4, 5, 6
i = 其他任意值时,输出 Default

 比较有意思的是,switch后面的表达式甚至不是必需的,比如下面的例子:

switch { case 0 <= Num && Num <= 3: fmt.Printf("0-3") case 4 <= Num && Num <= 6: fmt.Printf("4-6") case 7 <= Num && Num <= 9: fmt.Printf("7-9") 
}
  • 只有在case中明确添加fallthrough关键字,才会继续执行紧跟的下一个case
  • 可以不设定switch之后的条件表达式,在此种情况下,整个switch结构与多个 if...else...的逻辑作用等同
1.3 循环语句
  • 在条件表达式中也支持多重赋值
func ForCase() {sum := 0for i := 0; i < 10; i++ {sum += i}fmt.Println(sum) // 45// 在条件表达式中也支持多重赋值,如下表示:a := []int{1, 2, 3, 4, 5, 6}for i, j := 0, len(a)-1; i < j; i, j = i+1, j-1 {a[i], a[j] = a[j], a[i]}fmt.Println(a) // [6 5 4 3 2 1]
}

另外,Go语言的for循环同样支持continue和break来控制循环,但是它提供了一个更加高级的break,可以选择中断哪一个循环,如下例子:

for j := 0; j < 5; j++ { for i := 0; i < 10; i++ { if i > 5 { break JLoop } fmt.Println(i) } 
} 
JLoop:
本例中, break 语句终止的是 JLoop 标签处的外层循环。
2.函数
  • 在Go中,函数的基本组成为:关键字func、函数名、参数列表、返回值、函数体和返回语句
2.1函数定义
用一个最简单的加法函数来进行详细说明:
package mainimport ("fmt"
)
import "errors"func Add(a int, b int) (ret int, err error) {if a < 0 || b < 0 { // 假设这个函数只支持两个非负数字的加法err = errors.New("Should be non-negative numbers!")return}return a + b, nil // 支持多重返回值
}func main() {res, err := _case.Add(1, 2)if err != nil {return}fmt.Println(res)
}
如果参数列表中若干个相邻的参数类型的相同,比如上面例子中的 a b ,则可以在参数列表中省略前面变量的类型声明,如下所示:
func Add(a, b int)(ret int, err error) { // ... 
}
如果返回值列表中多个返回值的类型相同,也可以用同样的方式合并。
如果函数只有一个返回值,也可以这么写:
func Add(a, b int) int { // ... 
}

2.2 函数调用

函数调用非常方便,只要事先导入了该函数所在的包,就可以直接按照如下所示的方式调用函数:
import "mymath"// 假设Add被放在一个叫mymath的包中// ... 
c := mymath.Add(1, 2)
先牢记这样的规则:
  • 小写字母开头的函数只在本包内可见,大写字母开头的函数才能被其他包使用
  • 这个规则也适用于类型和变量的可见性
2.3 不定参数
(1)不定参数类型
不定参数: 指函数传入的参数个数为不定数量
首先需要将函数定义为接受不定参数类型:
func myfunc(args ...int) { for _, arg := range args { fmt.Println(arg) } 
}// 这段代码的意思是,函数myfunc()接受不定数量的参数,这些参数的类型全部是int,
// 所以它可以用如下方式调用:myfunc(2, 3, 4) 
myfunc(1, 3, 7, 13)

形如...type格式的类型只能作为函数的参数类型存在,并且必须是最后一个参数

它是一 个语法糖(syntactic sugar),即这种语法对语言的功能并没有影响,但是更方便程序员使用。通常来说,使用语法糖能够增加程序的可读性,从而减少程序出错的机会。

从内部实现机理来说说,类型...type本质上是一个数组切片,也就是[]type,这也是为什么上面的参数args可以用for循环来获得每个传入的参数

假如没有...type这样的语法糖,开发者将不得不这么写:

func myfunc2(args []int) { for _, arg := range args { fmt.Println(arg) } 
}
从函数的实现角度来看,这没有任何影响,该怎么写就怎么写。但从调用方来说,情形则完
全不同:
myfunc2([]int{1, 3, 7, 13})
们不得不加上 []int{} 来构造一个数组切片实例。但是有了 ...type这个语法糖,我们就不用自己来处理了。
(2)不定参数的传递
package mainimport ("fmt"
)func processFunc(args ...int) {for i, _ := range args {args[i] += 1}fmt.Println(args)
}func myfunc(args ...int) {// 按照原样传递processFunc(args...)// 传递片段,实际上任意的int slice都可以传进去processFunc(args[1:]...)
}func main() {myfunc(2, 3, 4)
}

(3)任意类型的不定参数

之前的例子中将不定参数类型约束为 int ,如果你希望传任意类型,可以指定类型为 interface{}。下面是 Go 语言标准库中 fmt.Printf() 的函数原型:
func Printf(format string, args ...interface{}) { // ... 
}

用interface{}传递任意类型数据是Go语言的惯例用法。使用interface{}仍然是类型安全的,和C/C++不太一样

func MyPrintf(args ...interface{}) {for _, arg := range args {switch arg.(type) {case int:fmt.Println(arg, "is an int value.")case string:fmt.Println(arg, "is a string value.")case int64:fmt.Println(arg, "is an int64 value.")default:fmt.Println(arg, "is an unknown type.")}}
}func main() {var v1 int = 1var v2 int64 = 234var v3 string = "heheda"var v4 float32 = 1.234_case.MyPrintf(v1, v2, v3, v4)
}

(4)多返回值

与C、C++和Java等开发语言的一个极大不同在于,Go语言的函数或者成员的方法可以有多个返回值,这个特性能够让我们写出比其他语言更优雅,更简洁的代码。

比如File.Read()函数就可以同时返回读取的字节数和错误信息

如果读取文件成功,则返回值中的n为读取的字节数,err为nil,否则err为具体的出错信息:

func (file *File) Read(b []byte) (n int, err Error)

同样,从上面的方法原型可以看到,我们还可以给返回值命名,就像函数的输入参数一样。

  • 返回值被命名之后,它们的值在函数开始的时候被自动初始化为空
  • 在函数中执行不带任何参数的return语句时,会返回对应的返回值变量的值

Go语言并不需要强制命名返回值,但是命名后的返回值可以让代码更清晰,可读性更强,同时也可用于文档

如果调用方调用了一个具有多返回值的方法,但是却不想关心其中的某个返回值,可以简单地用一个下划线"_"来跳过这个返回值,比如下面的代码表示调用者在读文件的时候不想关心Read()函数返回的错误码:

n, _ := f.Read(buf)

(5)匿名函数与闭包

① 匿名函数是指不需要定义函数名的一种函数实现方式,匿名函数由一个不带函数名的函数声明和函数体组成,如下所示:

func NMFunc() {f := func(x, y int) int {return x + y}fmt.Println(f(1, 2)) // 输出: 3// 创建一个通道replyChan := make(chan int)// 启动一个新的 Goroutine,执行匿名函数,并向通道发送数据go func(ch chan int) {ch <- 42}(replyChan) // 花括号后直接跟参数列表,表示调用// 从通道接收数据ack := <-replyChan// 打印接收到的数据fmt.Println(ack) // 输出: 42// 关闭通道close(replyChan)
}

②闭包:Go的匿名函数是一个闭包

基本概念:闭包是可以包含自由(未绑定到特定对象)变量的代码块,这些变量不在这个代码块内或者任何全局上下文中定义,而是在定义代码块的环境中定义。要执行的代码块(由于自由变量包含 在代码块中,所以这些自由变量以及它们引用的对象没有被释放)为自由变量提供绑定的计算环境(作用域)。

闭包的价值:在于可以作为函数对象或者匿名函数

Go语言中的闭包:Go语言中的闭包同样会引用到函数外的变量,闭包的实现确保只要闭包还被使用,那么被闭包引用的变量会一直存在

closure.go

package main 
import ( "fmt" 
) 
func main() { var j int = 5 a := func()(func()) { var i int = 10 return func() { fmt.Printf("i, j: %d, %d\n", i, j) } }() a() j *= 2 a() 
}
上述例子的执行结果是:
上述例子的执行结果是:
i, j: 10, 5
i, j: 10, 10
在上面的例子中,变量a指向 的闭包函数引用了局部变量 i j i的值被隔离,在闭包外不能被修改,改变j的值以后,再次调用a,发现结果是修改过的值。
2.4 错误处理
漂亮的错误处理规范是 Go 语言最大的亮点之一
(1)error接口
Go语言引入了一个关于错误处理的标准模式,即error接口,该接口的定义:
type error interface { Error() string
}
对于大多数函数,如果要返回错误,大致上都可以定义为如下模式,将 error 作为多种返回
值中的最后一个,但这并非是强制要求:
func Foo(param int)(n int, err error) { // ... 
}
调用时的代码建议按如下方式处理错误情况:
n, err := Foo(0) 
if err != nil { // 错误处理
} else { // 使用返回值n 
}

自定义的error类型

步骤一:定义一个用于承载错误信息的类型

func (e *PathError) Error() string { return e.Op + " " + e.Path + ": " + e.Err.Error() 
}

当然,这里是一个完整的例子展示了如何定义一个自定义的 PathError 类型,并在文件操作中使用这个类型在出错时进行错误处理和信息提取

package mainimport ("fmt""os"
)// PathError 自定义错误类型
type PathError struct {Op   stringPath stringErr  error
}// 实现 error 接口的 Error 方法
func (e *PathError) Error() string {return e.Op + " " + e.Path + ": " + e.Err.Error()
}// 函数用来模拟文件操作,并返回一个自定义的 PathError 错误
func simulateFileOperation() error {// 尝试获取文件信息,这里故意对一个不存在的文件操作_, err := os.Stat("a.txt")if err != nil {// 返回自定义的 PathError 错误return &PathError{Op:   "stat",Path: "a.txt",Err:  err,}}return nil
}func main() {// 调用模拟文件操作的函数err := simulateFileOperation()if err != nil {// 把错误断言为 *PathError 类型if e, ok := err.(*PathError); ok && e.Err != nil {// 获取 PathError 类型变量 e 中的其他信息并处理fmt.Printf("Operation: %s\n", e.Op)fmt.Printf("Path: %s\n", e.Path)fmt.Printf("Error: %s\n", e.Err)fmt.Printf("Full Error: %s\n", e.Error())} else {// 其他类型的错误fmt.Println(err)}} else {// 文件操作成功fmt.Println("File operation succeeded")}
}

在这个示例中:

① 定义了一个PathError类型,这个类型有Op(操作)、Path(路径)和Err(底层错误)三个字段

② 实现了PathError类型的Error方法,以满足error接口

③ 编写了一个simulateFileOperation函数,这个函数尝试获取一个不存在的文件的状态,并返回一个自定义的PathError错误

④ 在main函数中,调用simulateFileOperation函数,接收并处理这个自定义错误

这样,当你运行这个示例时,如果文件a.txt不存在,会输出:

Operation: stat
Path: a.txt
Error: stat a.txt: no such file or directory
Full Error: stat a.txt: stat a.txt: no such file or directory

这个示例展示了如何自定义错误类型并在程序中使用它来进行错误处理和信息提取

这就是Go中error类型的使用方法,与其他语言中的异常相比,Go的处理相对比较直观,简单

2.5 defer 

  • 关键字defer是Go语言引入的一个非常有意思的特性

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/383506.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

leetcode 2236.判断根节点是否等于字节点

1.题目要求: 给你一个 二叉树 的根结点 root&#xff0c;该二叉树由恰好 3 个结点组成&#xff1a;根结点、左子结点和右子结点。如果根结点值等于两个子结点值之和&#xff0c;返回 true &#xff0c;否则返回 false 。2.思路: 直接数组前序遍历&#xff0c;然后判断后面两个…

【C++】:红黑树深度剖析 --- 手撕红黑树!

目录 前言一&#xff0c;红黑树的概念二&#xff0c;红黑树的性质三&#xff0c;红黑树节点的定义四&#xff0c;红黑树的插入操作4.1 第一步4.2 第二步4.3 插入操作的完整代码 五&#xff0c;红黑树的验证六&#xff0c;实现红黑树的完整代码五&#xff0c;红黑树与AVL树的比较…

【接口自动化_08课_Pytest+Yaml+Allure框架】

上节课一些内容 的补充 1、openxl这个方法&#xff0c;第一个元素是从1开始的&#xff0c;不是从0开始 回写的列在程序里写的是11&#xff0c;是因为是固定值 一、1. Yaml入门及应用 1、什么是yaml YAML&#xff08;/ˈjməl/&#xff0c;尾音类似camel骆驼&#xff09;是一…

单向链表

目录 思维导图&#xff1a; 学习内容&#xff1a; 1. 链表的引入 1.1 顺序表的优缺点 1.1.1 优点 1.1.2 不足 1.1.3 缺点 1.2 链表的概念 1.2.1 链式存储的线性表叫做链表 1.2.2 链表的基础概念 1.3 链表的分类 2. 单向链表 2.1 节点结构体类型 2.2 创建链表 2.…

EXCEL 排名(RANK,COUNTIFS)

1.单列排序 需求描述&#xff1a;如有下面表格&#xff0c;需要按笔试成绩整体排名。 解决步骤&#xff1a; 我们使用RANK函数即可实现单列整体排名。 Number 选择第一列。 Ref 选择这一整列&#xff08;CtrlShift向下箭头、再按F4&#xff09;。 "确定"即可计算…

图像分类算法概述:深度学习方法

图像分类算法概述&#xff1a;深度学习方法 图像分类是计算机视觉中的一个基本任务&#xff0c;近年来随着深度学习的发展&#xff0c;图像分类算法取得了巨大的进步。本文将概述主要的深度学习图像分类算法。 #mermaid-svg-fkTtkPLl9ahuVT6w {font-family:"trebuchet ms…

BGP选路之Preferred value

原理概述 当一台BGP路由器中存在多条去往同一目标网络的BGP路由时&#xff0c;BGP协议会对这些BGP路由的属性进行比较&#xff0c;以确定去往该目标网络的最优BGP路由&#xff0c;然后将该最优BGP路由与去往同一目标网络的其他协议路由进行比较&#xff0c;从而决定是否将该最优…

元组(tuple)

目录 一、基本介绍 1、元组(tuple)可以存放多个不同数据类型&#xff0c;元组是不可变序列 2、元组也是一种数据类型 二、元组的定义 1、元组的定义 2、代码说明 三、元组的使用 1、元组使用语法 2、举例说明 3、代码演示&#xff0c;访问/获取第三个数据/元素 四、…

SpringBoot集成Kaptcha验证码

Hi &#x1f44b;, Im shy 有人见尘埃&#xff0c;有人见星辰 1. 什么是Kaptcha验证码? Kaptcha是一个强大的开源Java验证码生成库,由Google开发。它能够生成高度可配置的图片验证码,主要用于防止自动化程序滥用web应用,提高应用的安全性。 2. Kaptcha的主要特性 Kaptch…

AMEsim液压阀伯德图绘制方法

之前也在液压圈论坛里面发过类似的贴子&#xff0c;具体可以看这个网址&#x1f6aa;&#x1f449;&#xff1a;如何得出说明书里面的伯德图曲线&#xff1f;&#xff0c;回复的人还是比较少&#xff0c;这个方法重要信息是参考百度文库这篇文章&#x1f6aa;&#x1f449;&…

相机的内参与外参

目录 一、相机的内参二、相机的外参 一、相机的内参 如下图所示是相机的针孔模型示意图&#xff1a; 光心O所处平面是相机坐标系(O&#xff0c;P)&#xff0c;像素平面所在坐标系为像素坐标系(O’&#xff0c;P’)。 焦距f&#xff1a;O到O’的距离 相机的内参表示的是相机坐标…

文本编辑三巨头(grep)

目录 正则表达式 元字符 grep 案例 我在编写脚本的时候发现&#xff0c;三个文本编辑的命令&#xff08;grep、sed、awk&#xff0c;被称为文本编辑三剑客&#xff0c;我习惯叫它三巨头&#xff09;用的还挺多的&#xff0c;说实话我一开始学的时候也有些懵&#xff0c;主要…

【实现100个unity特效之8】使用ShaderGraph实现2d贴图中指定部分局部发光效果

最终效果 寒冰法师 火焰法师 文章目录 最终效果寒冰法师火焰法师 素材一、功能分析实现方法基本思路Unity的Bloom后处理为什么关键部位白色&#xff1f;最终结果 二、 新建URP项目三、合并图片四、使用PS制作黑白图片方法一 手动涂鸦方法二 魔棒工具1. 拖入图片进PS&#xff0…

环信+亚马逊云科技服务:助力出海AI社交应用扬帆起航

随着大模型技术的飞速发展&#xff0c;AI智能体的社交体验得到了显著提升&#xff0c;AI社交类应用在全球范围内持续火热。尤其是年轻一代对新技术和新体验的热情&#xff0c;使得AI社交产品在海外市场迅速崛起。作为领先的即时通讯解决方案提供商&#xff0c;环信与亚马逊云科…

# Redis 入门到精通(九)-- 主从复制(2)

Redis 入门到精通&#xff08;九&#xff09;-- 主从复制&#xff08;2&#xff09; 一、redis 主从复制–数据同步阶段注意事项 1、数据同步阶段 master 说明 1&#xff09;如果 master 数据量巨大&#xff0c;数据同步阶段应避开流量高峰期&#xff0c;避免造成 master 阻…

掌握Rust:函数、闭包与迭代器的综合运用

掌握Rust&#xff1a;函数、闭包与迭代器的综合运用 引言&#xff1a;解锁 Rust 高效编程的钥匙函数定义与模式匹配&#xff1a;构建逻辑的基石高阶函数与闭包&#xff1a;代码复用的艺术迭代器与 for 循环&#xff1a;高效数据处理的引擎综合应用案例&#xff1a;构建一个简易…

JavaSE--基础语法--继承和多态(第三期)

一.继承 1.1我们为什么需要继承? 首先&#xff0c;Java中使用类对现实世界中实体来进行描述&#xff0c;类经过实例化之后的产物对象&#xff0c;则可以用来表示现实中的实体&#xff0c;但是 现实世界错综复杂&#xff0c;事物之间可能会存在一些关联&#xff0c;那在设计程…

Redis的应用场景及类型

目录 一、Redis的应用场景 1、限流 2、分布式锁 3、点赞 4、消息队列 二、Redis类型的命令及用法 1、String类型 2、Hash类型 3、List类型 4、Set类型 5、Zset类型 6、Redis工具类 Redis使用缓存的目的就是提升读写性能 实际业务场景下&#xff0c;我们就可以把 Mys…

Mysql数据库第四次作业

mysql> create table student(sno int primary key auto_increment,sname varchar(30) not null unique,Ssex varchar(2) check (Ssex男 or Ssex女) not null,Sage int not null,Sdept varchar(10) default计算机 not null); mysql> create table Course(Con int primar…

pytest的安装和介绍和 Exit Code 含义

pytest 准备工作&#xff08;在cmd里&#xff09;&#xff1a; 1安装 pip install -U pytest2验证安装 pytest --version # 会展示当前已安装版本3其他的 显示可用的内置函数参数 pytest --fixtures通过命令行查看帮助信息及配置文件选项 pytest --help一、pytets框架中的…