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

news2024/11/15 17:21:04

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 := 0
	for 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 main

import (
	"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 main

import (
	"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 = 1
	var v2 int64 = 234
	var 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 main

import (
	"fmt"
	"os"
)

// PathError 自定义错误类型
type PathError struct {
	Op   string
	Path string
	Err  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.coloradmin.cn/o/1948005.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

docker产生日志过大优化

1、Docker容器启动后日志存放位置 #cat /var/lib/docker/containers/容器ID/容器ID-json.log #echo >/var/lib/docker/containers/容器ID/容器ID-json.log临时清除日志 注&#xff1a;echo一个空进去&#xff0c;不需要重启容器&#xff0c;但如果你直接删除这个日志&…

【ROS2】高级:安全-理解安全密钥库

目标&#xff1a;探索位于 ROS 2 安全密钥库中的文件。 教程级别&#xff1a;高级 时间&#xff1a;15 分钟 内容 背景安全工件位置 公钥材料 私钥材料域治理政策 安全飞地 参加测验&#xff01; 背景 在继续之前&#xff0c;请确保您已完成设置安全教程。 sros2 包可以用来创…

vue3在元素上绑定自定义事件弹出虚拟键盘

最近开发中遇到一个需求: 焊接机器人的屏幕上集成web前端网页, 但是没有接入键盘。这就需要web端开发一个虚拟键盘,在网上找个很多虚拟键盘没有特别适合,索性自己写个简单的 图片: 代码: (代码可能比较垃圾冗余,也没时间优化,凑合看吧) 第一步:创建键盘组件 为了方便使用…

鸿蒙 动态共享包HSP的创建和引用

1.什么是动态共享包HSP HSP&#xff08;Harmony Shared Package&#xff09;是动态共享包&#xff0c;可以包含代码、C库、资源和配置文件&#xff0c;通过HSP可以实现代码和资源的共享。HSP不支持独立发布&#xff0c;而是跟随其宿主应用的APP包一起发布&#xff0c;与宿主应…

arinc664总线协议

AFDX总线协议简介 &#xff08;1&#xff09;AFDX的传输速率高&#xff1a;带宽100MHZ&#xff0c;远远高于其他的类型的航空总线。&#xff08;2&#xff09;AFDX网络的鲁棒性高&#xff1a;AFDX的双冗余备份网络可以在某一个网络出现故障时&#xff0c;仍能正常通讯。 其中…

【C语言】结构体详解 -《探索C语言的 “小宇宙” 》

目录 C语言结构体&#xff08;struct&#xff09;详解结构体概览表1. 结构体的基本概念1.1 结构体定义1.2 结构体变量声明 2. 结构体成员的访问2.1 使用点运算符&#xff08;.&#xff09;访问成员输出 2.2 使用箭头运算符&#xff08;->&#xff09;访问成员输出 3. 结构体…

【NPU 系列专栏 2 -- NVIDIA 的 H100 和 H200 是什么?】

请阅读【嵌入式及芯片开发学必备专栏】 文章目录 NVIDIA H100 和 H200 芯片NVIDIA H100 芯片简介NVIDIA H100 主要特点NVIDIA H100 应用场景NVIDIA H100 使用举例NVIDIA H200 芯片简介NVIDIA H200 主要特点NVIDIA H200 应用场景NVIDIA H200 使用举例Summary NVIDIA H100 和 H20…

How do I increase max_new_tokens

题意&#xff1a;怎样增加 max_new_tokens 的值 问题背景&#xff1a; Im facing this error while running my code: 运行代码时遇到如下错误&#xff1a; ValueError: Input length of input_ids is 1495, but max_length is set to 20. This can lead to unexpected…

lua 游戏架构 之 游戏 AI (五)ai_autofight_find_way

这段Lua脚本定义了一个名为 ai_autofight_find_way 的类&#xff0c;继承自 ai_base 类。 lua 游戏架构 之 游戏 AI &#xff08;一&#xff09;ai_base-CSDN博客文章浏览阅读238次。定义了一套接口和属性&#xff0c;可以基于这个基础类派生出具有特定行为的AI组件。例如&…

Vue实现简单小案例

一、创建文件夹 二、引用vue.js <script src"../js/vue.js"></script> 三、准备一个容器 <div id"app"><h1>Hello,{{name}}</h1> </div> 四、创建实例 <script>new Vue({el:"#app", //el用于指…

tcache attack

Tcache Attack tcache让堆利用更加简单&#xff1a; tcache回顾&#xff1a; 在 tcache 中新增了两个结构体&#xff0c;分别是 tcache_entry 和 tcache_perthread_struct&#xff1a; /* We overlay this structure on the user-data portion of a chunk when the chunk is …

数据库概念以及增删改

1.概念 、 2.通用语法 3.数据增删改

mac大文件清理软件哪个好 mac大文件怎么清理 苹果电脑清理软件推荐免费

mac采用固态硬盘技术&#xff0c;数据存储和系统响应速度优势明显&#xff0c;但是mac的内存弊端同样体现在其固态硬盘的技术&#xff0c;导致用户无法通过机械硬盘进行扩充内存。而我们日常使用电脑会产生大量系统垃圾、用户缓存等文件&#xff0c;平时下载的电影和大型游戏安…

通信原理-实验六:实验测验

实验六 实验测验 一&#xff1a;测验内容和要求 测试需要完成以下几个步骤&#xff1a; 配置好以下网络图&#xff1b;占总分10%&#xff08;缺少一个扣一分&#xff09;根据下面图配置好对应的IP和网关以及路由等相关配置&#xff0c;保证设备之间连通正常&#xff1b;占总…

【日常记录】【JS】对一个数组,按照某个字段的值,进行分组

文章目录 1. 前言2. lodash 的分组3. Object.groupBy()参考链接 1. 前言 在开发中&#xff0c;经常会遇到一组数据&#xff0c;要按照某个字段进行分组&#xff0c;这个时候会有很多种方法&#xff0c;可以使用 forEach、reduce、等其他方法 reduce 方法 function groupBy(arr…

JS基础知识学习笔记全

JS基础知识学习笔记全 一、引入方式 1、内部脚本 &#xff08;一般定义在body下面会改善执行速度&#xff09; <body></body><!-- 内部脚本 --><script>/* 打开页面警告框显示的内容 */alert(helloJS);</script>2、外部脚本 外部专门新建一…

Redis-主从模式

目录 前言 一.主从节点介绍 二.配置redis主从结构 二.主从复制 四.拓扑结构 五.数据同步 全量复制&#xff08;Full Sync Replication&#xff09; 局部复制&#xff08;Partial Replication&#xff09; Redis的学习专栏&#xff1a;http://t.csdnimg.cn/a8cvV 前言 …

H3CNE(vlan与子接口技术)

目录 10.1 vlan间通信技术 示例一&#xff08;多臂路由&#xff09;&#xff1a; 10.2 子接口技术 示例二&#xff08;子接口技术&#xff09;&#xff1a; 10.3 vlannif接口技术 10.3.1 三层交换机与VLANNIF技术 示例三VLANNIF配置&#xff08;将交换机当成路由器使用&…

鸿蒙仓颉语言【cryptocj 库】RC2、 RC4 、AES对称加密算法

2 提供RC2、 RC4 、AES对称加密算法 前置条件&#xff1a;NA 场景&#xff1a; 支持对称加密算法。 约束&#xff1a;RC2密钥长度一般16字节&#xff0c;加密块长度8字节&#xff1b;AES加密块长度16字节 性能&#xff1a; 支持版本几何性能持平 可靠性&#xff1a; NA …

pytest:4种方法实现 - 重复执行用例 - 展示迭代次数

简介&#xff1a;在软件测试中&#xff0c;我们经常需要重复执行测试用例&#xff0c;以确保代码的稳定性和可靠性。在本文中&#xff0c;我们将介绍四种方法来实现重复执行测试用例&#xff0c;并显示当前迭代次数和剩余执行次数。这些方法将帮助你更好地追踪测试执行过程&…