【Go】Go语言中延迟函数、函数数据的类型、匿名函数、闭包等高阶函数用法与应用实战

在这里插入图片描述

✨✨ 欢迎大家来到景天科技苑✨✨

🎈🎈 养成好习惯,先赞后看哦~🎈🎈

🏆 作者简介:景天科技苑
🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。
🏆《博客》:Python全栈,Golang开发,PyQt5和Tkinter桌面开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi,flask等框架,云原生K8S,linux,shell脚本等实操经验,网站搭建,数据库等分享。

所属的专栏:Go语言开发零基础到高阶实战
景天的主页:景天科技苑

在这里插入图片描述

文章目录

  • 函数进阶
    • 1. 延迟函数(defer)
    • 2. 函数的数据类型
    • 3. 函数的本质
    • 4. 匿名函数
    • 5. 回调函数
    • 6. 闭包
      • 闭包计算斐波那契数

函数进阶

1. 延迟函数(defer)

defer语句在Go语言中是一个强大的特性,它允许你延迟函数的执行直到包含它的函数即将返回。这通常用于清理资源、解锁互斥锁、记录时间、关闭文件等操作。
比如说我们打开一个文件,我们判断它有没关闭,我们希望所有代码执行完,最后调用defer函数来关闭文件

语法格式:
defer 函数调用
注意:

defer语句会将其后的函数调用压入一个栈中,当外层函数即将返回时,这些被defer的函数会按照后进先出(LIFO)的顺序执行。
defer可以在函数中多次使用,以注册多个需要延迟执行的函数。

package mainimport "fmt"// defer
func main() {f("1")fmt.Println("2")//其他语句执行完,再执行defer包含的函数defer f("3")fmt.Println("4")
}func f(s string) {fmt.Println(s)
}

可以看到3最后打印
在这里插入图片描述
流程分析
在这里插入图片描述

defer函数或者方法:一个函数或方法的执行被延迟了

  • 你可以在函数中添加多个defer语句当函数执行到最后时,这些defer语句会按照逆序执行,最后该函数返回,特别是当你在进行一些打开资源的操作时i/o 流,遇到错误需要提前返回,在返回前你需要关闭相应的资源,不然很容易造成资源泄露等问题
  • 如果有很多调用 defer,那么 defer 是采用 后进先出(栈) 模式。
  • 你可以在函数中添加多个defer语句,当没有加defer的语句执行完毕后,这些defer语句会按照逆序执行

多个defer案例:

package mainimport "fmt"// defer 作用:处理一些善后的问题,比如错误,文件、网络流关闭等等操作。
// 特点,多个defer的问题
// 不加defer的语句还是按顺序从上而下执行
// 你可以在函数中添加多个defer语句,当没有加defer的语句执行完毕后,这些defer语句会按照逆序执行
func main() {f("1")defer fmt.Println("2")defer f("3")fmt.Println("4")defer f("5")fmt.Println("6")defer f("7")fmt.Println("8")
}func f(s string) {fmt.Println(s)
}

在这里插入图片描述

defer存在传递参数:
在defer的时候,函数其实已经被调用了,但是没有执行。参数是已经传递进去的了

package mainimport "fmt"// defer传参的调用时机
func main() {n := 10fmt.Println("main n=", n)// 在defer的时候,函数其实已经被调用了,但是没有执行。参数是已经传递进去的了defer ff(n) // 问题,defer延迟执行函数,参数时什么时候传递进去?在其他地方虽然n已经加1了,ff函数最后执行,但是ff函数中的n还是10,说明参数是先加载进来,只是没执行函数n++fmt.Println("main end n=", n)defer ff(n) //如果ff在n加1之后defer延迟执行,参数中的n是加1后的n
}func ff(n int) {fmt.Println("ff函数中n=", n)
}

在这里插入图片描述

defer后续常用场景:程序会报错: 异常(程序执行的时候突然报错了)、错误(我们开发的时候知道的预期错误)

善后工作:defer 处理异常。

2. 函数的数据类型

  • func (xxxx,xxx) xxx,xxxx
  • 函数也是一种数据类型,可以定义函数类型的变量
package mainimport "fmt"// 函数是什么(数据类型)
func main() {a := 10.01fmt.Printf("%T\n", a) // 查看变量的类型b := [4]int{1, 2, 3, 4}fmt.Printf("%T\n", b) // 查看变量的类型c := truefmt.Printf("%T\n", c) // 查看变量的类型// 函数的类型func1()                   // 带了括号是函数的调用fmt.Printf("%T\n", func1) // 查看变量的类型 func()fmt.Printf("%T\n", func2) // 查看变量的类型 func(int, int, ...string) (int, int)// func(int, int) (int, int)// func(int, int, ...string) (int, int)//var fun3 func(int, int, ...string) (int, int)fun3 := func2r1, r2 := fun3(1, 2, "111")fmt.Println(r1, r2)// 函数在Go语言中本身也是一个数据类型,加了() 是调用函数,不加(), 函数也是一个变量,可以赋值给别人。// 函数的类型就等于该函数创建的类型,他也可以赋值给
}// 无参无返回值的函数
func func1() {}// 有参有返回值的函数
func func2(a, b int, c ...string) (int, int) {return 0, 0
}

在这里插入图片描述

3. 函数的本质

函数在Go语言中不是一个简单的调用或者接收结果的。

函数在Go中是一个符合类型,可以看做是一个特殊的变量。var 定义吗,赋值。类型相同即可

函数类型的样子 :var f1 函数名(参数) 结果

变量名:指向一段内存 (num --> 0x11111aaaa)

函数名:指向一段函数体的内存地址,是一种特殊类型的变量。我们可以将一个函数赋值给另外一个类型相同的函数
在这里插入图片描述

4. 匿名函数

Go语言支持匿名函数,即没有名称的函数。匿名函数通常用于实现局部的、一次性的功能,或者作为高阶函数的参数传递。
匿名函数,在函数体后增加(),调用了这个函数,匿名函数只能一次。
将匿名函数进行赋值,就可以实现多次调用。

Go语言支持函数式编程

  • 将匿名函数作为另外一个函数的参数,回调函数
  • 将匿名函数作为另外一个函数的返回值,可以形成闭包结构
package mainimport "fmt"// 匿名变量 (没有名字的变量)
// 匿名函数 (没有名字的函数)
func main() {// 正常的调用f12()f2 := f12 // 函数赋值给另外一个函数f2()// f12  f2 本质指向了同一个内存空间,空间中的代码一致  {fmt.Println("我是f12函数")}// 匿名函数,在函数体后增加(),调用了这个函数,匿名函数只能一次。func() {fmt.Println("我是一个匿名函数1")}()// 将匿名函数进行赋值,就可以实现多次调用。f3 := func() {fmt.Println("我是一个匿名函数2")}//将匿名函数赋值给变量后,可以直接通过变量调用f3()// 匿名函数是否可以添加参数和返回值func(a, b int) {fmt.Println("a,b")}(1, 2)// 将匿名函数的返回值定义给变量。r1 := func(a, b int) int {return a + b}(1, 2)fmt.Println(r1)// 由于Go语言中的函数是一个特殊的变量,支持匿名操作// Go语言支持函数式编程// - 将匿名函数作为另外一个函数的参数,回调函数// - 将匿名函数作为另外一个函数的返回值,可以形成闭包结构}
func f12() {fmt.Println("我是f12函数")
}

在这里插入图片描述

5. 回调函数

高阶函数:可以将一个函数作为另外一个函数的参数。

fun1()

fun2(fun1)

fun1 函数作为 fun2 函数的参数

fun2函数,叫做高阶函数,接收了另外一个函数作为参数的函数

fun1函数,叫做回调函数,作为另外一个函数的参数

package mainimport "fmt"// 回调函数
func main() {// 函数调用r1 := add(1, 2)fmt.Println("r1是:", r1)// 高阶函数调用r2 := oper(1, 2, add)fmt.Println("r2是:", r2)r3 := oper(1, 2, sub)fmt.Println("r3是:", r3)// 匿名函数fun1 := func(a, b int) int {return a * b}r4 := oper(1, 2, fun1) // 调用匿名函数 *fmt.Println("r4是:", r4)// 能够直接传递匿名函数r5 := oper(8, 2, func(a int, b int) int {if b == 0 {fmt.Println("除数不能为0")return 0}return a / b})fmt.Println("r5是:", r5)
}// 运算 (运算的数字,运算操作)
// 高阶函数,参数是接收另外一个函数
func oper(a, b int, fun func(int, int) int) int {//打印出的fun是每次传入的函数内存地址fmt.Println(a, b, fun)r := fun(a, b)return r
}func add(a, b int) int {return a + b
}
func sub(a, b int) int {return a - b
}

在这里插入图片描述

函数也可作为可变参数传参
在这里插入图片描述

6. 闭包

一个外层函数中,有内层函数,该内层函数中,会操作外层函数的局部变量
并且该外层函数的返回值就是这个内层函数。
这个内层函数和外层函数的局部变量,统称为闭包结构。

局部变量的生命周期就会发生改变,正常的局部变量会随着函数的调用而创建,随着函数的结束而销毁
但是闭包结构中的外层函数的局部变量并不会随着外层函数的结束而销毁,因为内层函数还在继续使用

package mainimport "fmt"// 是一种特殊的结构:闭包结构,违反了程序正常的生命周期。合法的使用。程序允许的一种特殊结构,变量作用域升级了。// 什么时候用闭包: js (xxxxxxx.html   引用大量的第三方库:10个js库,js库中很多变量名是冲突的)
// js 很多框架都是闭包结构的,防止变量冲突,全局变量污染// 我的代码里面的变量就不会和你代码里面的变量冲突了。解决一些变量作用域冲突的问题。/*
闭包结构:
一个外层函数中,有内层函数,该内层函数中,会操作外层函数的局部变量并且该外层函数的返回值就是这个内层函数。在闭包结构中:局部变量的生命周期就会发生改变,
正常的局部变量会随着函数的调用而创建,随着函数的结束而销毁
但是闭包结构中的外层函数的局部变量并不会随着外层函数的结束而销毁,因为内层函数还在继续使用.// 由于垃圾回收期不会将闭包中的变量销毁,可能会造成内存泄漏。
*/// 你的代码变量和你同事的变量冲突了,解决。 i 新建一个变量。 第三方库中的代码都是闭包结构实现的导出。
var i int = 10func main() {r1 := increment()fmt.Println(r1) // 返回的是一个 increment() 内层函数,还没有执行// -- 执行这个内层函数//v1 := r1()fmt.Println(v1)v2 := r1()fmt.Println(v2)fmt.Println(r1())fmt.Println(r1())fmt.Println(r1())// 你写的代码是对的,但是结果不对,你的变量被污染了fmt.Println("--------------------------")// r2和r1指向同一个地址r2 := increment() // 再次调用的时候 ,i = 0v3 := r2()fmt.Println(v3) // 1//因为我们内层还是用i,还存在引用,系统不会销货这个i,保护,单独作用r1fmt.Println(r1()) // 6     // 这里的i 并没有随着 第二次创建就被销毁归0,而是在内层函数继续调用着。fmt.Println(r2()) // 2// r1 名字 ----> 内存地址 &r1fmt.Printf("%p\n", &r1)fmt.Printf("%p\n", &r2)}// 自增函数
// increment() 函数返回值为  func() int 类型
func increment() func() int { // 外层函数,项目(很多的全局变量)// 定义一个局部变量i := 0// 在外层函数内部定义一个匿名函数,给变量自增并返回。fun := func() int {i++return i}return fun
}

在这里插入图片描述

如果我们想使用闭包结构来解决全局变量污染的问题,那我们就可以写一个闭包结构来创建执行的函数。

通过这个闭包结构创建的函数内部的变量,都在这个函数中作用,不会和其他函数冲突。

**闭包结果的返回值是一个函数。**这个函数可以调用闭包结构中的变量。

package mainimport "fmt"// 是一种特殊的结构:闭包结构,违反了程序正常的生命周期。合法的使用。程序允许的一种特殊结构,变量作用域升级了。// 什么时候用闭包: js (xxxxxxx.html   引用大量的第三方库:10个js库,js库中很多变量名是冲突的)
// js 很多框架都是闭包结构的,防止变量冲突,全局变量污染// 我的代码里面的变量就不会和你代码里面的变量冲突了。解决一些变量作用域冲突的问题。/*
闭包结构:
一个外层函数中,有内层函数,该内层函数中,会操作外层函数的局部变量并且该外层函数的返回值就是这个内层函数。在闭包结构中:局部变量的生命周期就会发生改变,
正常的局部变量会随着函数的调用而创建,随着函数的结束而销毁
但是闭包结构中的外层函数的局部变量并不会随着外层函数的结束而销毁,因为内层函数还在继续使用.// 由于垃圾回收期不会将闭包中的变量销毁,可能会造成内存泄漏。
*/// 你的代码变量和你同事的变量冲突了,解决。 i 新建一个变量。 第三方库中的代码都是闭包结构实现的导出。
var i int = 10func main() {r1 := increment()fmt.Println(r1) // 返回的是一个 increment() 内层函数,还没有执行。直接打印函数名得到的是名字的地址// -- 执行这个内层函数//v1 := r1()fmt.Println(v1)v2 := r1()fmt.Println(v2)fmt.Println(r1())fmt.Println(r1())fmt.Println(r1())// 你写的代码是对的,但是结果不对,你的变量被污染了fmt.Println("--------------------------")// r2和r1指向同一个地址r2 := increment() // 再次调用的时候 ,i = 0v3 := r2()fmt.Println(v3) // 1//因为我们内层还是用i,还存在引用,系统不会销货这个i,保护,单独作用r1fmt.Println(r1()) // 6     // 这里的i 并没有随着 第二次创建就被销毁归0,而是在内层函数继续调用着。fmt.Println(r2()) // 2// r1 名字 ----> 内存地址 &r1fmt.Printf("%p\n", &r1)fmt.Printf("%p\n", &r2)}// 自增函数
// increment() 函数返回值为  func() int 类型
//通过increment()可以创建无数个函数,让incremnet()变量不会影响每个函数的使用func increment() func() int { // 外层函数,项目(很多的全局变量)// 定义一个局部变量i := 0// 在外层函数内部定义一个匿名函数,给变量自增并返回。fun := func() int {i++return i}return fun
}

在这里插入图片描述

闭包计算斐波那契数

package mainimport "fmt"// 返回一个“返回类型为int的函数” ,将闭包函数作为值返回
func fibonacci() func() int {a, b := 1, 1 //定义初始的两个值return func() int {c := aa, b = b, a+breturn c}
}func main() {f := fibonacci()//计算前10个斐波那契数列for i := 0; i < 10; i++ {fmt.Println(f())}
}

在这里插入图片描述

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

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

相关文章

Mysql | 知识 | 理解是怎么加锁的

文章目录 一、怎么加行级锁的&#xff1f;二、唯一索引加锁2.1 唯一索引等值查询1、记录存在的情况2、记录不存在的情况 2.2 唯一索引范围查询a. 针对「大于」的范围查询b. 针对「大于等于」的范围查询的情况。c. 「小于」范围查询&#xff0c;记录「不存在」表中的情况d. 「小…

音乐革命:揭秘树莓派如何重塑 Korg 合成器的未来

采用快速紧凑的 Raspberry Pi 计算模块3&#xff08;Raspberry Pi Compute Module 3&#xff09;的简易设置&#xff0c;为Korg备受推崇的高端乐器提供了一种经济高效的解决方案。 解决方案&#xff1a;Compute Module 3 企业规模&#xff1a;大型企业 行业&#xff1a;音乐…

uniapp小程序,使用腾讯地图获取定位

本篇文章分享一下在实际开发小程序时遇到的需要获取用户当前位置的问题&#xff0c;在小程序开发过程中经常使用到获取定位功能。uniapp官方也提供了相应的API供我们使用。 官网地址&#xff1a;uni.getLocation(OBJECT)) 官网获取位置的详细介绍这里就不再讲述了&#xff0c;大…

【LeetCode】每日一题 2024_9_14 从字符串中移除星号(模拟)

前言 每天和你一起刷 LeetCode 每日一题~ LeetCode 启动&#xff01; 今天的题目曾经的我做过了 . . . 又是复习的一天 题目&#xff1a;从字符串中移除星号 代码与解题思路 func removeStars(s string) string {// 本题的核心&#xff1a;生成的输入保证总是可以执行题面中…

大数据-136 - ClickHouse 集群 表引擎详解1 - 日志、Log、Memory、Merge

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 目前已经更新到了&#xff1a; Hadoop&#xff08;已更完&#xff09;HDFS&#xff08;已更完&#xff09;MapReduce&#xff08;已更完&am…

【绿盟科技盟管家-注册/登录安全分析报告】

前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 暴力破解密码&#xff0c;造成用户信息泄露短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨大&#xff0c;造成亏损无底洞…

秒懂:父子进程与bash(命令行参数)的关系

情景解析&#xff1a; 执行以下代码&#xff1a; #include<string.h> #include<unistd.h> int g_val 100000;int main() {int key7;printf("I am father process, pid: %d, ppid: %d, g_val: %d\n", getpid(), getppid(), g_val);sleep(5);pid_t id f…

现代 Web 开发全攻略:Node.js、npm、Webpack、Vue.js 和 Element-UI 的实战指南

现代 Web 开发全攻略&#xff1a;Node.js、npm、Webpack、Vue.js 和 Element-UI 的实战指南 一 . Node.js1.1 什么是 Node.js ?1.2 Node.js 的安装1.3 快速入门1.3.1 控制台输出1.3.2 使用函数1.3.3 模块化编程 二 . npm 包管理器2.1 什么是 npm ?2.2 npm 命令2.2.1 初始化工…

护眼灯品牌排行第一名出炉!盘点2024年世界公认十大护眼灯

中国拥有全球最多的近视人口&#xff0c;我国学生的近视发病率位居世界第二&#xff0c;人数更是居于首位。如今&#xff0c;越来越多的孩子出现近视现象&#xff0c;许多家长认为这是由于繁重的课业和不健康的用眼习惯所致&#xff0c;但实际上&#xff0c;他们往往忽视了一个…

数据分析-前期数据处理

今天找到一份关于医学体检的数据&#xff0c;在数据分析前期工作需要对数据做处理&#xff0c;在这里我们对原始数据做一些处理&#xff0c;将数据处理为可分析的标准数据。下一篇文章做数据的分析。数据想要获取的话可以到我的资源下载。1 数据读取 import pandas as pd data…

SQL Server详细使用教程(包含启动SQL server服务、建立数据库、建表的详细操作) 非常适合初学者

文章目录 目录 前言 一、启动SQL server服务的三种方法 1.不启动SQL server服务的影响 2.方法一&#xff1a;利用cmd启动SQL server服务 3.方法二&#xff1a;利用SQL Server配置管理器启动SQL server服务 4.方法三&#xff1a;在服务管理器中启动SQL server服务 二、建立数据库…

震撼!AI实时生成游戏,每秒20帧,谷歌扩散模型最新突破一夜爆火,附论文介绍和GitHub代码

震撼&#xff01;AI实时生成游戏&#xff0c;每秒20帧&#xff0c;谷歌扩散模型最新突破一夜爆火&#xff0c;附论文介绍和GitHub代码。 “比Sora还震撼”&#xff0c;AI可以实时生成游戏了&#xff01; 谷歌DeepMind打造出了首个完全AI驱动的实时游戏引擎——GameNGen。 在单…

SpringBoot集成MyBatis-PlusDruid

目录 MyBatis-Plus简介 实例演示 创建Springboot项目 初始化Springboot项目 添加关键依赖 application.properties添加相关配置 启动类 编写实体类 编写mapper接口 条件构造器 分页插件 自定义 SQL 映射 MyBatis-Plus简介 MyBatis-Plus简介‌MyBatis-Plus‌&…

RDD2022 道路瑕疵检测数据集

RDD2022 道路瑕疵数据集 txt标签或者xml标签 一共23767张图片 D00 D01 D20 D40四类 D00纵向裂缝 D10横向裂缝 D20网状裂缝 D40坑洞。 RDD2022 道路瑕疵检测数据集介绍 数据集概述 RDD2022&#xff08;Road Defect Detection 2022&#xff09;是一个专门用于道路瑕疵检测的数…

力扣之1777.每家商店的产品价格

文章目录 1. 1777.每家商店的产品价格1.1 题干1.2 建表1.3 题解1.4 结果截图 1. 1777.每家商店的产品价格 1.1 题干 表&#xff1a;Products -------------------- | Column Name | Type | -------------------- | product_id | int | | store | enum | | price | int | ---…

HarmonyOS 是如何实现一次开发多端部署 -- HarmonyOS自学1

一次开发多端部署遇到的几个关键问题 为了实现“一多”的目标&#xff0c;需要解决如下三个基础问题&#xff1a; 问题1&#xff1a;页面如何适配 不同设备间的屏幕尺寸、色彩风格等存在差异&#xff0c;页面如何适配。 问题2&#xff1a;功能如何兼容 不同设备的系统能力…

《深度学习》OpenCV 高阶 图像直方图、掩码图像 参数解析及案例实现

目录 一、图像直方图 1、什么是图像直方图 2、作用 1&#xff09;分析图像的亮度分布 2&#xff09;判断图像的对比度 3&#xff09;检测图像的亮度和色彩偏移 4&#xff09;图像增强和调整 5&#xff09;阈值分割 3、举例 二、直方图用法 1、函数用法 2、参数解析…

C++——深部解析哈希

好久不见给大家分享一张图片吧 目录 前言 二、库文件 1、哈希冲突 2 哈希函数 3、闭散列 三 、闭散列的实现和底层逻辑 1、哈希表&#xff08;闭散列&#xff09;的定义 2、哈希表&#xff08;闭散列&#xff09;的插入 3、哈希表&#xff08;闭散列&#xff09;的查找 4.哈希表…

【Unity踩坑】No cloud project ID was found by the Analytics SDK

在编译默认的URP 2D项目时&#xff0c;出现这样一个错误&#xff1a;No cloud project ID was found by the Analytics SDK. This means Analytics events will not be sent. Please make sure to link your cloud project in the Unity editor to fix this problem. 原因&…

JavaScript 基础 - 第16天_AJAX入门

文章目录 Day01_Ajax入门目录学习目标01.AJAX 概念和 axios 使用目标讲解小结 02.认识 URL目标讲解小结 03.URL 查询参数目标讲解小结 04.案例-查询-地区列表目标讲解小结 05.常用请求方法和数据提交目标讲解小结 06.axios 错误处理目标讲解小结 07.HTTP 协议-请求报文目标讲解…