Go语言学习Day5:函数(下)

news/2024/4/28 4:51:31/文章来源:https://blog.csdn.net/qq_51646682/article/details/137078406

名人说:莫愁千里路,自有到来风。 ——钱珝
创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊)

目录

      • 1、本质、数据类型与延迟函数
        • ①函数的本质
        • ②函数的数据类型
        • ③defer延迟函数
      • 2、匿名、回调函数与闭包
        • ①匿名函数
        • ②回调函数
        • ③闭包
      • 3、小结

上一篇博客中主要了解了函数的概念、定义、参数以及递归函数等内容,在本篇博客中,我们将接着探讨Go语言中的一些函数概念,包括函数的本质、数据类型、延迟执行函数、匿名函数、回调函数以及闭包。内容上,将分为两大部分来陈述,每一部分不仅会介绍相关的理论知识,还会通过语法格式和完整的代码案例来帮助大家更好地理解。

1、本质、数据类型与延迟函数

①函数的本质

在Go语言中,函数更加强大,可以像其他类型一样被赋值给变量,可以作为参数传递给其他函数,也可以作为其他函数的返回值。所以函数的本质在这里你可以看做是具有一定功能的代码块,也可以看做一种变量。这些函数设定大大增强了Go语言的灵活性和表达能力。

语法格式:

func 函数名(参数列表) (返回值列表) {// 函数体
}

接下来让我们看一个案例,回顾一下上篇内容所学的函数定义及调用,同时也能从案例中再次思忖函数的本质。

案例1:定义并调用简单函数

package mainimport "fmt"//求和函数
func sum(a int, b int) int {return a + b
}//主函数
func main() {result := sum(5, 7)fmt.Println("5 + 7 =", result)
}

image-20240327120756472

从上面可以看出,其实这些函数都具有一定功能,而且用花括号{}包裹了起来,比如main函数,程序的入口,就好比树干,人的大脑,而自定义函数类似于树枝,人的各个器官。

image-20240327121844884

接下来咱们再看一个案例,你可以发现函数在go中简直就是测量仪器中的万用表。

案例2:《论函数的多样性》

package mainimport "fmt"func main() {fmt.Printf("%T\n", testF)var f5 func(int, int)f5 = testF //此处赋值的是testF函数的引用,即f5可直接引用testF,因此二者指向同一块地址fmt.Println(f5)fmt.Println(testF)f5(1, 2)
}//func() 本身就是一个数据类型
//func如果不加(),函数本质就是一个变量func testF(a, b int) {fmt.Println(a, b)
}

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

②函数的数据类型

在Go语言中,函数也有自己的类型,这个类型由函数的签名决定,包括参数的类型和返回值的类型。

语法格式:

定义一个函数类型:

type 函数类型名 func(参数类型列表) 返回值类型列表

案例:函数类型

package mainimport "fmt"// 定义一个函数类型
type operation func(int, int) int// 实现加法函数
func add(a int, b int) int {return a + b
}func main() {var op operation = addresult := op(3, 4)fmt.Println("3 + 4 =", result)
}

image-20240327122611108

关于这句代码"var op operation = add",在当时看到的时候有些懵,这是啥,仔细捋一捋,才弄明白。首先var op operation = add,var来声明变量的,operation是自定义的函数类型,再考虑到之前var a int的意思是定义声明a为变量且为int类型,那这就好理解了,var op operation与之类似,意思是定义声明op为变量且为operation类型,而operation则是一个函数类型,所以精简下来就是,定义声明op为变量且为函数类型,并将add赋值给op。

因此这行代码执行后,op变量实际上就是一个函数,具体到这个例子中,它是add函数的一个引用或者说是别名

你可以通过调用op来执行add函数,如op(3, 4)会调用add函数,传入3和4作为参数,执行加法操作,并返回结果7。

③defer延迟函数

defer关键字用于延迟一个函数或者方法的执行defer语句会将其后面跟随的函数调用推迟到包含该defer语句的函数执行完毕时执行。

语法格式:

defer 函数调用

案例1:使用defer语句,延迟hello world

package mainimport "fmt"func main() {defer fmt.Println("world")fmt.Println("hello")
}

image-20240327123602897

通过延迟函数可以很神奇的发现,放在前面的world居然居后输出了,思来想去,不得其解,这究竟原因在何呢?通过查询一番,找到了答案:

Go语言为每个goroutine维护了一个延迟调用栈。当执行到一个defer语句时,后面的函数调用不会立即执行,而是被放入到当前goroutine的延迟调用栈中。这些被延迟的函数调用会按照后进先出(LIFO)的顺序执行,也就是说,最后被延迟的函数会最先执行。

一看到栈,很多小伙伴可能与俺一样能够明白其中一二了,那这样做又有什么优势呢?(没用加它干嘛,还得学,学不完的知识ing)

原来,这种基于栈的延迟调用机制能让资源管理变得简单而直观。咱们开发者不必担心资源释放的具体时机,只需在获取资源后立即通过defer来定义释放资源的逻辑。

巴拉巴拉一大堆…

总之,这样可以减少资源泄露的风险,同时使代码更加清晰和易于维护。

接下来再看一个案例,巩固一下所学概念:

案例2:使用defer语句,延迟自增

package mainimport "fmt"func main() {a := 10fmt.Println("a=", a)//go语言中,使用defer关键字来延迟一个函数或方法的执行defer f(a) //由于defer的影响,函数在最后执行,但参数此时已经传递进去,因此最后的值仍为10a++fmt.Println("end a=", a)
}func f(s int) {fmt.Println("函数里面的a的值s为:", s)
}

image-20240327143227512

2、匿名、回调函数与闭包

①匿名函数

匿名函数是没有名称的函数(就好比论坛里的匿名发言,问卷里的匿名表单),可以在需要函数类型的地方直接定义和使用。

语法格式:

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

案例1:使用匿名函数

package mainimport "fmt"func main() {sum := func(a int, b int) int {return a + b}fmt.Println(sum(3, 4)) // 输出:7
}

image-20240327143453354

案例2:函数式编程

package mainimport "fmt"func main() {test1()//函数本身也可以看做是一个变量f2 := test1f2()//匿名函数f3 := func() {fmt.Println("我是f3函数")}f3()//匿名函数自己调用自己func() {fmt.Println("我是f4函数")}()//匿名函数自己调用自己,可以有参数的func(a, b int) {fmt.Println(a, b)fmt.Println("我是f5函数")}(1, 2)//参数+返回值r1 := func(a, b int) int {fmt.Println("我是f5函数")return a + b}(1, 2)fmt.Println(r1)
}//go语言支持函数式编程:
//1、将匿名函数作为另外一个函数的参数,回调函数
//2、将匿名函数作为另外一个函数的返回值,可以形成闭包结构func test1() {fmt.Println("我是f2函数")
}
②回调函数

回调函数是作为参数传递给其他函数的函数,这允许一种灵活的方式来传入代码,实现类似插件的功能。

语法格式:

func 函数名(参数列表, 回调函数参数) (返回值列表) {// 函数体中调用回调函数
}

案例1:使用回调函数

package mainimport "fmt"func calculate(a int, b int, operation func(int, int) int) int {return operation(a, b)
}func main() {add := func(a int, b int) int {return a + b}result := calculate(5, 3, add)fmt.Println("5 + 3 =", result)
}

image-20240327143659465

案例2:高阶函数与回调函数

//创作者:Code_流苏(CSDN)
package mainimport "fmt"// 高阶函数和回调函数、// 高阶函数
// 指至少满足下列一种条件的函数:
// 接受一个或多个函数作为参数。
// 返回一个函数。// 回调函数
// 回调函数是被作为参数传递到另一个函数中,并且在那个函数内部被调用以完成某种任务或行为的函数。
// 回调是一种实现异步编程的技巧,允许某个操作的完成时能够通知或影响另一个函数的执行。
func main() {r1 := add1(1, 2)fmt.Println(r1) //1 + 2 = 3r2 := oper(3, 4, add1)fmt.Println(r2) //3 + 4 = 7r3 := oper(8, 4, sub)fmt.Println(r3) //8 - 4 = 4r4 := oper(8, 4, func(a int, b int) int {if b == 0 {fmt.Println("除数不能为0")return 0}return a / b})fmt.Println(r4) //8 / 4 = 2
}// 高阶函数 可以接受一个函数或多个函数作为参数
func oper(a, b int, fun func(int, int) int) int {r := fun(a, b)return r
}func add1(a, b int) int {return a + b
}func sub(a, b int) int {return a - b
}

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

③闭包

闭包是一个函数值,它引用了函数体之外的变量。这种机制使得函数可以访问并操作函数外部的变量。这个函数可以访问和赋值这些外部变量,并且即使外部函数的执行已经完成,闭包仍然可以访问这些变量。这使得闭包成为实现数据封装和管理状态的强大工具。特点如下:

  1. **函数:在 Go 语言中,函数可以被当作变量处理,可以作为参数传递,也可以作为返回值。
  2. 词法作用域:闭包函数能够引用定义它的函数中的变量。
  3. 状态封装:闭包允许封装状态。即使定义它的函数已经返回,闭包仍然能够操作这些状态。

语法格式:

func 外部函数名(外部函数参数) func(内部函数参数列表) 返回值类型 {// 外部函数体return func(内部函数参数列表) 返回值类型 {// 内部函数体}
}

案例1:使用闭包实现累加器

package mainimport "fmt"func accumulator(value int) func(int) int {return func(number int) int {value += numberreturn value}
}func main() {acc := accumulator(10)fmt.Println(acc(5))  // 10 + 5 = 15 故输出:15fmt.Println(acc(10)) // 15 + 10 = 25 因此输出:25
}

image-20240327150059394

image-20240327144154388

案例2:计数

package mainimport "fmt"// 创建一个返回闭包的函数
func counter() func() int {count := 0// 闭包函数return func() int {count += 1return count}
}func main() {myCounter := counter()fmt.Println(myCounter())  // 输出: 1fmt.Println(myCounter())  // 输出: 2anotherCounter := counter()fmt.Println(anotherCounter()) // 输出: 1fmt.Println(anotherCounter()) // 输出: 2
}

image-20240327144222713

在这个例子中,counter 函数返回了一个闭包,这个闭包操作外部函数中的 count 变量。即使 counter 函数的执行完成后,这些闭包仍然能够访问并修改 count 变量。不过,要注意,每次调用 counter 时,都会创建一个新的 count 变量和新的闭包,因此 myCounteranotherCounter 操作的是不同的 count 变量。

案例3:自增

//创作者:Code_流苏(CSDN)
package mainimport "fmt"// 在 Go 语言中,闭包(Closure)是一种特殊的函数;
// 它可以捕获其定义时所在作用域中的变量。闭包是由函数和与其相关的引用环境组合而成的实体。
// 简单来说,闭包允许你在一个函数内部定义另一个函数,并且这个内部函数可以访问外部函数的变量。
func main() {r1 := increment()fmt.Println(r1)v1 := r1()fmt.Println(v1) //1v2 := r1()fmt.Println(v2)   //2fmt.Println(r1()) //3fmt.Println(r1()) //4fmt.Println(r1()) //5//由于闭包结构的出现,本应该销毁的r1中的临时局部变量并未被销毁掉,//所以下面输出r1时,值在原来5的基础上自增1,变为了6r2 := increment()v3 := r2()fmt.Println(v3)   //1fmt.Println(r1()) //6fmt.Println(r2()) //2
}// 自增函数
func increment() func() int {//局部变量ii := 0//定义一个匿名函数,给变量自增并返回fun := func() int { //内层函数,没有执行的//局部变量的生命周期发生了变化i++return i}return fun
}

image-20240327144402027

3、小结

  • 函数的本质是什么?一种灵活的、能够被当作数据处理的行为单元(变量、参数等等),函数式编程的理念
  • 函数的数据类型,函数类型 func_type
  • 延迟函数,原理,延迟调用栈
  • 匿名函数,无名氏,但确实实实在在的函数,俺是一块砖,哪里需要哪里搬
  • 回调函数,是函数,也是参数,作为参数传递
  • 闭包,函数式编程,匿名函数实现,闭包被调用时,即使其外部函数已经返回,闭包仍然可以访问和操作这些捕获的变量。(跨越了平常的生命周期(通常,在一个函数执行结束后,其局部变量的生命周期也随之结束))

以上就是Go语言中关于函数的本质、数据类型、延迟执行函数、匿名函数、回调函数以及闭包的讲解和案例,本篇内容就到这里,后续学习记录会陆续更新ing…

很感谢你能看到这里,如有相关疑问,还请下方评论留言。
Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊)
希望本篇内容能对大家有所帮助,如果大家喜欢的话,请动动手点个赞和关注吧,非常感谢你们的支持!

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

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

相关文章

Go——map操作及原理

一.map介绍和使用 map是一种无序的基于key-value的数据结构,Go语言的map是引用类型,必须初始化才可以使用。 1. 定义 Go语言中,map类型语法如下: map[KeyType]ValueType KeyType表示键类型ValueType表示值类型 map类型的变量默认…

班级综合测评管理系统的设计与实现|Springboot+ Mysql+Java+ B/S结构(可运行源码+数据库+设计文档)

本项目包含可运行源码数据库LW,文末可获取本项目的所有资料。 推荐阅读100套最新项目持续更新中..... 2024年计算机毕业论文(设计)学生选题参考合集推荐收藏(包含Springboot、jsp、ssmvue等技术项目合集) 目录 1. …

docker 的网络管理

docker应用自带了三种类型的网络,然后我们自己也能自定义网络 roottest-virtual-machine:~# docker network ls NETWORK ID NAME DRIVER SCOPE 4c3e28760cff bridge bridge local afd1493dc119 host host local 5f200e2eaf22 n…

AOP切入点表达式基本格式

版权声明 本文原创作者:谷哥的小弟作者博客地址:http://blog.csdn.net/lfdfhl 官方地址 https://docs.spring.io/spring-framework/reference/core/aop/ataspectj/pointcuts.html AOP切入点表达式基本格式如下: execution(modifiers-patte…

Vscode创建php项目

1.安装中文插件(可安装可不安装) 2.安装主题(可安装可不安装) 3.安装和php相关的插件 4.打开文件夹 5.路由操作 查看项目中的route路由 浏览器中访问think 隐藏index.php入口文件 访问ThinkPHP5.1开发手册,复制apa…

React-1-jsx基础-事件绑定-样式处理

一.JSX基础-概念和本质 1.1 什么是JSX JSX是JavaScript和XML(HTML)的缩写,表示在JS代码中编写HTML模版结构,它是React中编写UI模版的方式 优势: 1. HTML的声明式模版写法 2. JS的可编程能力 JSX的本质: JSX并不是标…

[openGL] qt5版本+mingw编译Assimp库+调用

目录 一 版本 二 编译问题 三 CMAKE准备 四 开始编译 4.1 准备Assimp源码 4.2 编译工具准备 4.3 生成Assimp库 4.4 使用Assimp 4.4.1 准备 4.4.2 加载模型 4.4.3 模型效果 一 版本 Assimp官网上已经停止更新截至在3.3.1版本,但是这个版本编译是最稳定的,较新的版本…

WORDPRESS从WORD复制粘贴公式

整合教程:WordPress插件包整合教程 WordPaster支持自动上传本地图片文件,自动上传Word文档中的图片 步骤与效果: 1.打开word文档,复制word文档内容 2.在网页中打开编辑器页面,点击“粘贴本地文件,Word文档”按钮上传…

GBase8a-GDCA认证考试-复习参考题

个人能力有限,正确率97%(97分)。 请注意甄别,根据所学知识综合判断,欢迎指出错误答案。 欢迎学习天津南大通用数据技术股份有限公司|GBASE-致力于成为用户最信赖的数据库产品供应商 免费参加认证培训:为…

Visio中存在问题的解决方法

公式缩放 mathtype公式在visio缩放之后,出现了变形。 解决方法:每次输入公式都通过 插入->对象->mathType Equation 新建一个公式。可以避免 注:网上有的说在word中使用mathtype编写公式,之后复制到visio中。 插入波形 选择…

Java的IDEA的工程管理

模块和包的图标: 举个例子: IDEA中创建包: 如图所示,com.LBJ的意思是在com包中创建子包LBJ 参见: IDEA中项目、模块和包的关系_idea中模块和项目-CSDN博客

应用层协议之DNS协议

一.应用层协议的相关数据传输格式 1.文本字符串格式 应用层主要是自定义协议,以点外卖为例: 客户点开软件,就是应用程序和服务器之间进行网络通信交互。请求和响应可以如下设置 请求:用户信息,位置信息&#xff0c…

Vue模块化开发步骤—遇到的问题—解决办法

目录 1.npm install webpack -g 2.npm install -g vue/cli-init 3.初始化vue项目 4.启动vue项目 Vscode初建Vue时几个需要注意的问题-CSDN博客 1.npm install webpack -g 全局安装webpack 直接命令提示符运行改指令会报错,operation not permitted 注意&#…

【QT入门】 Qt代码创建布局之水平布局、竖直布局详解

往期回顾: 【QT入门】 Qt实现自定义信号-CSDN博客 【QT入门】 Qt自定义信号后跨线程发送信号-CSDN博客 【QT入门】 Qt内存管理机制详解-CSDN博客 【QT入门】 Qt代码创建布局之水平布局、竖直布局详解 先看两个问题: 1、ui设计器设计界面很方便&#xf…

1学习使用axios

一、axios介绍: axios 是一个基于 Promise 的 HTTP 客户端,用于浏览器和 Node.js。它提供了一种简单的方法来发送 HTTP 请求,并且具有很多实用的功能,使得网络请求变得更加方便和可靠。 以下是 axios 的一些主要特点和功能&…

python判断当前日期是全年哪一天

设计者:ISDF 版本:v3..0 日期:04/01/2019设计者:ISDF 版本:v4..0 日期:03/27/2024 import datetime#闰年判断函数 def ys_leep_year(year):ys_leep Falseif (year % 400 0) or ((year % 4 0) and (year …

【每日力扣】452. 用最少数量的箭引爆气球与763. 划分字母区间

🔥 个人主页: 黑洞晓威 😀你不必等到非常厉害,才敢开始,你需要开始,才会变的非常厉害。 452. 用最少数量的箭引爆气球 有一些球形气球贴在一堵用 XY 平面表示的墙面上。墙面上的气球记录在整数数组 points &#xff0…

SpringBoot学习之ElasticSearch下载安装和启动(Windows版)(三十)

本文先写windows下的下载安装和启动,后续有时间再补充其他环境下(Mac、Linux、Docker)的,这里我们后续对ElasticSearch简称为ES,读者习惯这一称呼就好。 一,ES下载 可以百度【ElasticSearch官网】或者直接点击这里的ES官网下载地址:​​​​​ Download Elasticsearch…

【 MyBatis 】| 关于多表联查返回 List 集合只查到一条的 BUG

目录 一. 🦁 写在前面二. 🦁 探索过程2.1 开端 —— 开始写 bug2.2 发展 —— bug 完成2.3 高潮 —— bug探究2.4 结局 —— 效果展示 三. 🦁 写在最后 一. 🦁 写在前面 今天又是 BUG 气满满的一天,一个 xxxMapper.xm…

聊聊低代码产品的应用场景

随着数字化转型的不断深入,企业对于快速开发和迭代软件应用的需求也越来越迫切。而在这样的背景下,低代码产品应运而生,成为了一种热门的技术解决方案。本文将解读低代码产品的定义并探讨其应用场景。 一、低代码产品的定义 低代码产品是一种…