go语言函数进阶

news2024/11/18 15:37:56

1.变量作用域

全局变量

全局变量是定义在函数外部的变量,它在程序整个运行周期内都有效。 在函数中可以访问到全局变量。

package main

import "fmt"

//定义全局变量num
var num int64 = 10

func testGlobalVar() {
	fmt.Printf("num=%d\n", num) //函数中可以访问全局变量num
}
func main() {
	testGlobalVar() //num=10
}

局部变量

局部变量又分为两种: 函数内定义的变量无法在该函数外使用,例如下面的示例代码main函数中无法使用testLocalVar函数中定义的变量x:

func testLocalVar() {
	//定义一个函数局部变量x,仅在该函数内生效
	var x int64 = 100
	fmt.Printf("x=%d\n", x)
}

func main() {
	testLocalVar()
	fmt.Println(x) // 此时无法使用变量x
}

如果局部变量和全局变量重名,优先访问局部变量。

package main

import "fmt"

//定义全局变量num
var num int64 = 10

func testNum() {
	num := 100
	fmt.Printf("num=%d\n", num) // 函数中优先使用局部变量
}
func main() {
	testNum() // num=100
}

接下来我们来看一下语句块定义的变量,通常我们会在if条件判断、for循环、switch语句上使用这种定义变量的方式。

func testLocalVar2(x, y int) {
	fmt.Println(x, y) //函数的参数也是只在本函数中生效
	if x > 0 {
		z := 100 //变量z只在if语句块生效
		fmt.Println(z)
	}
	//fmt.Println(z)//此处无法使用变量z
}

还有我们之前讲过的for循环语句中定义的变量,也是只在for语句块中生效:

func testLocalVar3() {
	for i := 0; i < 10; i++ {
		fmt.Println(i) //变量i只在当前for语句块中生效
	}
	//fmt.Println(i) //此处无法使用变量i
}

2.函数类型与变量

定义函数类型

我们可以使用type关键字来定义一个函数类型,具体格式如下:

type calculation func(int, int) int

上面语句定义了一个calculation类型,它是一种函数类型,这种函数接收两个int类型的参数并且返回一个int类型的返回值。

简单来说,凡是满足这个条件的函数都是calculation类型的函数,例如下面的add和sub是calculation类型。

func add(x, y int) int {
	return x + y
}

func sub(x, y int) int {
	return x - y
}

add和sub都能赋值给calculation类型的变量。

var c calculation
c = add

函数类型变量

我们可以声明函数类型的变量并且为该变量赋值:

func main() {
	var c calculation               // 声明一个calculation类型的变量c
	c = add                         // 把add赋值给c
	fmt.Printf("type of c:%T\n", c) // type of c:main.calculation
	fmt.Println(c(1, 2))            // 像调用add一样调用c

	f := add                        // 将函数add赋值给变量f
	fmt.Printf("type of f:%T\n", f) // type of f:func(int, int) int
	fmt.Println(f(10, 20))          // 像调用add一样调用f
}

3.高阶函数

高阶函数分为函数作为参数和函数作为返回值两部分。

函数作为参数

函数可以作为参数:

func add(x, y int) int {
	return x + y
}
func calc(x, y int, op func(int, int) int) int {
	return op(x, y)
}
func main() {
	ret2 := calc(10, 20, add)
	fmt.Println(ret2) //30
}

函数作为返回值

函数也可以作为返回值:

func do(s string) (func(int, int) int, error) {
	switch s {
	case "+":
		return add, nil
	case "-":
		return sub, nil
	default:
		err := errors.New("无法识别的操作符")
		return nil, err
	}
}

4.匿名函数和闭包

匿名函数

函数当然还可以作为返回值,但是在Go语言中函数内部不能再像之前那样定义函数了,只能定义匿名函数。匿名函数就是没有函数名的函数,匿名函数的定义格式如下:

func(参数)(返回值){
    函数体
}

匿名函数因为没有函数名,所以没办法像普通函数那样调用,所以匿名函数需要保存到某个变量或者作为立即执行函数:

func main() {
	// 将匿名函数保存到变量
	add := func(x, y int) {
		fmt.Println(x + y)
	}
	add(10, 20) // 通过变量调用匿名函数

	//自执行函数:匿名函数定义完加()直接执行
	func(x, y int) {
		fmt.Println(x + y)
	}(10, 20)
}

匿名函数多用于实现回调函数和闭包。

闭包

闭包指的是一个函数和与其相关的引用环境组合而成的实体。简单来说,闭包=函数+引用环境。 首先我们来看一个例子:

func adder() func(int) int {
	var x int
	return func(y int) int {
		x += y
		return x
	}
}
func main() {
	var f = adder()
	fmt.Println(f(10)) //10
	fmt.Println(f(20)) //30
	fmt.Println(f(30)) //60

	f1 := adder()
	fmt.Println(f1(40)) //40
	fmt.Println(f1(50)) //90
}

变量f是一个函数并且它引用了其外部作用域中的x变量,此时f就是一个闭包。 在f的生命周期内,变量x也一直有效。 闭包进阶示例1:

func adder2(x int) func(int) int {
	return func(y int) int {
		x += y
		return x
	}
}
func main() {
	var f = adder2(10)
	fmt.Println(f(10)) //20
	fmt.Println(f(20)) //40
	fmt.Println(f(30)) //70

	f1 := adder2(20)
	fmt.Println(f1(40)) //60
	fmt.Println(f1(50)) //110
}

闭包进阶示例2:

func makeSuffixFunc(suffix string) func(string) string {
	return func(name string) string {
		if !strings.HasSuffix(name, suffix) {
			return name + suffix
		}
		return name
	}
}

func main() {
	jpgFunc := makeSuffixFunc(".jpg")
	txtFunc := makeSuffixFunc(".txt")
	fmt.Println(jpgFunc("test")) //test.jpg
	fmt.Println(txtFunc("test")) //test.txt
}

闭包进阶示例3:

func calc(base int) (func(int) int, func(int) int) {
	add := func(i int) int {
		base += i
		return base
	}

	sub := func(i int) int {
		base -= i
		return base
	}
	return add, sub
}

func main() {
	f1, f2 := calc(10)
	fmt.Println(f1(1), f2(2)) //11 9
	fmt.Println(f1(3), f2(4)) //12 8
	fmt.Println(f1(5), f2(6)) //13 7
}

闭包其实并不复杂,只要牢记闭包=函数+引用环境

5.defer语句

Go语言中的defer语句会将其后面跟随的语句进行延迟处理。在defer归属的函数即将返回时,将延迟处理的语句按defer定义的逆序进行执行,也就是说,先被defer的语句最后被执行,最后被defer的语句,最先被执行。

举个例子:

func main() {
	fmt.Println("start")
	defer fmt.Println(1)
	defer fmt.Println(2)
	defer fmt.Println(3)
	fmt.Println("end")
}

输出结果:

start
end
3
2
1

由于defer语句延迟调用的特性,所以defer语句能非常方便的处理资源释放问题。比如:资源清理、文件关闭、解锁及记录时间等。

defer执行时机

在Go语言的函数中return语句在底层并不是原子操作,它分为给返回值赋值和RET指令两步。而defer语句执行的时机就在返回值赋值操作后,RET指令执行前。具体如下图所示:

defer执行时机

defer经典案例

阅读下面的代码,写出最后的打印结果。

func f1() int {
	x := 5
	defer func() {
		x++
	}()
	return x
}

func f2() (x int) {
	defer func() {
		x++
	}()
	return 5
}

func f3() (y int) {
	x := 5
	defer func() {
		x++
	}()
	return x
}
func f4() (x int) {
	defer func(x int) {
		x++
	}(x)
	return 5
}
func main() {
	fmt.Println(f1())
	fmt.Println(f2())
	fmt.Println(f3())
	fmt.Println(f4())
}

参考文章:

https://www.fansimao.com/1006299.html 

https://www.fansimao.com/1006315.html

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

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

相关文章

汽车网络安全dos, someip

汽车Cyber Security入门之DoS 攻防 - 知乎 3、SOME/IP-TP 近年来火热地谈论下一代EE架构和SOA的时候&#xff0c;总离不开SOME/IP这个进程间通讯协议。在许多应用场景中&#xff0c;需要通过UDP传输大型的SOME/IP有效载荷。鉴于在以太网上传输数据包的大小限制&#xff0c;SO…

多维时序 | Matlab实现DBO-BiLSTM蜣螂算法优化双向长短期记忆神经网络多变量时间序列预测

多维时序 | Matlab实现DBO-BiLSTM蜣螂算法优化双向长短期记忆神经网络多变量时间序列预测 目录 多维时序 | Matlab实现DBO-BiLSTM蜣螂算法优化双向长短期记忆神经网络多变量时间序列预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.Matlab实现DBO-BiLSTM多变量时间序…

Matlab|【完全复现】基于价值认同的需求侧电能共享分布式交易策略

目录 1 主要内容 2 部分程序 3 程序结果 4 下载链接 1 主要内容 该程序完全复现《基于价值认同的需求侧电能共享分布式交易策略》&#xff0c;针对电能共享市场的交易机制进行研究&#xff0c;提出了基于价值认同的需求侧电能共享分布式交易策略&#xff0c;旨在降低电力市…

面经基础版案例(路由,请求渲染,传参,组件缓存)

文章目录 1.案例效果分析2.配置一级路由&#xff08;首页&#xff0c;详情&#xff09;3.配置二级路由4.导航高亮效果5.首页的请求渲染6.传参&#xff08;查询参数 $ 动态路由&#xff09;7.详情页渲染8.组件缓存kepp-alive9.总结 1.案例效果分析 2.配置一级路由&#xff08;首…

实战 | OpenCV+OCR实现弧形文字识别实例(详细步骤 + 源码)

导 读 本文主要介绍基于OpenCV+OCR实现弧形文字识别实例,并给详细步骤和代码。源码在文末。 背景介绍 测试图如下,目标是正确识别图中的字符。图片来源: https://www.51halcon.com/forum.php?mod=viewthread&tid=6712 同样,论坛中已经给出了Halcon实现代码,…

web应用课——(第二讲:CSS)

目录 一、实战项目一&#xff1a;Acwing名片 二、实战项目二&#xff1a;Bilibili名片 三、样式定义方式 四、选择器 五、颜色 六、文本 七、字体 八、背景 九、边框 十、元素展示格式 十一、内边距与外边距 十二、盒子模型 十三、位置 十四、浮动 十五、flex布…

老代码为啥如此设计,我是如何解决编译失败的?

周末翻出三年前的杰作“jpy”&#xff0c;专为人工智能开发的dsl语言&#xff0c;不仅编译无法通过&#xff0c;而且一连串的疑问映入眼帘。这…这…这些文件/内容都是干啥的啊&#xff0c;为什么如此设计&#xff1f; 技术千万条&#xff0c;专研第一条&#xff0c;自嗨无笔记…

Lombok的详细教程

什么是lombok Lombok是一个Java库&#xff0c;它通过提供一组注释来简化Java类的开发。使用Lombok&#xff0c;开发人员可以通过在类或字段上添加注释来自动生成通用的方法&#xff0c;如getter、setter、equals、hashCode等。这样可以减少冗余的样板代码&#xff0c;提高开发效…

2024 年最佳 PDF 编辑器榜单:PDF 编辑的首选

您可能经常遇到或使用 PDF 文件 - 它们在现代跨平台世界中无处不在。大多数时候&#xff0c;在查看 PDF 时&#xff0c;您可以使用免费软件来阅读或评论这些文件。但如果您还需要编辑它们怎么办&#xff1f;这就是 PDF 编辑器的用武之地。 最好的 PDF 编辑器允许您编辑、创建、…

Bytebase 签约 Aptive,助力北美商住害虫控制服务领导者构建统一数据库操作平台

在数字化快速发展时代&#xff0c;有效的规范数据库管理对企业安全运营至关重要。近日&#xff0c;数据库 DevOps 团队协同管理工具 Bytebase 签约北美商住害虫控制服务的领导者 Aptive Environmental&#xff0c;旨在全面优化 Aptive Environmental 的数据库操作管理&#xff…

数据结构与算法教程,数据结构C语言版教程!(第六部分、数据结构树,树存储结构详解)四

第六部分、数据结构树&#xff0c;树存储结构详解 数据结构的树存储结构&#xff0c;常用于存储逻辑关系为 "一对多" 的数据。 树存储结构中&#xff0c;最常用的还是二叉树&#xff0c;本章就二叉树的存储结构、二叉树的前序、中序、后序以及层次遍历、线索二叉树、…

第一节课,用户管理--后端初始化,项目调通。二次翻工

一、代码下载 网址&#xff1a; 用户管理第一节课&#xff0c;阿里生成代码包-CSDN博客 二、项目步骤&#xff0c;参考从 网址&#xff1a; 一、第一节课&#xff0c;用户管理--后端初始化&#xff0c;项目调通-CSDN博客 从这里开始跟随 &#xff08;一&#xff09;、跟随…

eclipse用gerrit提交失败

1.gerrit简介 Gerrit&#xff0c;一种免费、开放源代码的代码审查软件&#xff0c;使用网页界面。利用网页浏览器&#xff0c;同一个团队的软件程序员&#xff0c;可以相互审阅彼此修改后的程序代码&#xff0c;决定是否能够提交&#xff0c;退回或者继续修改。它使用Git作为底…

精通Python第13篇—数据之光:Pyecharts旭日图的魔法舞台

文章目录 引言准备工作绘制基本旭日图调整颜色和样式添加交互功能定制标签和标签格式嵌套层级数据高级样式与自定义进阶主题&#xff1a;动态旭日图数据源扩展&#xff1a;外部JSON文件总结 引言 数据可视化在现代编程中扮演着重要的角色&#xff0c;而Pyecharts是Python中一个…

【数据分析】Excel中使用VBA进行宏编程

目录 0 准备工作1 VBA简介1.1 Excel VBA应用程序的构成1.2 事件驱动1.3 宏1.3.1 创建宏1.3.2 宏安全 2 VBA基础2.1 注释2.2 数据类型2.2.1 基本数据类型2.2.2 枚举类型2.2.3 用户自定义数据类型 2.2 变量2.3 常量2.4 运算符2.5 程序结构2.6 过程2.7 函数 3 Excel应用程序开发流…

配置华为交换机环路检测案例

知识改变命运&#xff0c;技术就是要分享&#xff0c;有问题随时联系&#xff0c;免费答疑&#xff0c;欢迎联系&#xff01; 厦门微思网络​​​​​​https://www.xmws.cn 华为认证\华为HCIA-Datacom\华为HCIP-Datacom\华为HCIE-Datacom 思科认证\CCNA\CCNP\CCIE Linux\RHCE…

给你一颗“定心丸”——记一次由线上事故引发的Log4j2日志异步打印优化分析

一、内容提要 自知是人外有人&#xff0c;天外有天&#xff0c;相信对于Log4j2的异步日志打印早有老师或者同学已是熟稔于心&#xff0c;优化配置更是信手拈来&#xff0c;为了防止我在这里啰里八嗦的班门弄斧&#xff0c;我先将谜底在此公布&#xff1a;_log4j2.asyncQueueFu…

【ArcGIS微课1000例】0098:查询河流流经过的格网

本实验讲述,ArcGIS中查询河流流经过的格网,如黄河流经过的格网、县城、乡镇、省份等。 文章目录 一、加载数据二、空间查询三、结果导出四、注意事项一、加载数据 加载实验配套数据0098.rar中的河流(黄河)和格网数据,如下图所示: 接下来,将查询河流流经过的格网有哪些并…

Python进阶(1) | 使用VScode写单元测试

Python进阶(1) | 单元测试 2024.01.28 VSCode: 1.85.1 Linux(ubuntu 22.04) 文章目录 Python进阶(1) | 单元测试1. 目的2. Python Profile3. 单元测试框架3.1 什么是单元测试3.2 选一个单元测试框架3.3 编写 Python 单元测试代码3.4 在 VSCode 里发现单元测试3.5 再写一个单元…

Linux的优先级说明

一、背景 在工作中&#xff0c;不少同学对nice&#xff0c;priority&#xff0c;schedue策略&#xff0c;实时优先级&#xff0c;普通进程优先级的概念混淆&#xff0c;导致最后的代码可能引入bug&#xff0c;本文将统一进行说明&#xff0c;部分内容参考网络大佬的文章 &…