Go语言之函数(func)进阶篇

news/2024/11/15 11:45:38/文章来源:https://www.cnblogs.com/yinzhengjie/p/18308556

目录
  • 一.init初始化函数
    • 1 初始化函数作用
    • 2 初始化函数定义案例
      • 2.1 初始化项目
      • 2.2 utils.go源代码
      • 2.3 main.go源代码
  • 二.匿名函数
    • 1 匿名函数概述
    • 2 匿名函数案例
  • 三.闭包函数
    • 1 闭包函数概述
    • 2 闭包函数案例之返回上级函数内部变量
    • 3 闭包函数案例之返回上级函数形参变量
    • 4 闭包函数返回多个匿名函数案例
    • 5 闭包函数实现给文件加后缀
  • 四.高阶函数
    • 1 高阶函数概述
    • 2 高阶函数案例之函数作为参数案例
    • 3 高阶函数案例之函数作为返回值
  • 五.defer关键字
    • 1 defer执行时机
    • 2 defer案例
    • 3 defer面试题
      • 3.1 观察代码手写运算结果1
      • 3.2 观察代码手写运算结果2

一.init初始化函数

1 初始化函数作用

- 1.init初始化函数可以用来进行初始化操作每个"*.go"源文件都可以包含一个init函数,该函数会在main函数执行前,被Go运行框架调用。- 2.全局变量定义,init函数,main函数的执行流程?顺序依次是: 全局变量定义,init函数,main函数。- 3.多个源文件都有init函数,如何执行?

2 初始化函数定义案例

2.1 初始化项目

image-20240716002954608

yinzhengjie@localhost 03-init % go mod init yinzhengjie-utlis
go: creating new go.mod: module yinzhengjie-utlis
go: to add module requirements and sums:go mod tidy
yinzhengjie@localhost 03-init % 

2.2 utils.go源代码

package utilsimport "fmt"var (Name   stringAge    intGender string
)func init() {fmt.Println("in utils package ... init ")Age = 18Name = "Jason Yin"Gender = "boy"
}

2.3 main.go源代码

package mainimport ("fmt"// 第1步: 先导入第三方包"yinzhengjie-utlis/utils"
)// 第2步: 全局变量定义
var number int = demo()func demo() int {fmt.Println("in demo ...")return 100
}// 第3步: 调用init函数
func init() {fmt.Println("init函数被调用...")
}// 第4步: 调用main函数
func main() {fmt.Println("main函数被调用...")fmt.Printf("姓名:%s 年龄:%d 性别: %s\n", utils.Name, utils.Age, utils.Gender)
}

二.匿名函数

1 匿名函数概述

- Go支持匿名函数,如果我们某个函数只是希望使用一次,可以考虑使用匿名函数。- 匿名函数使用方式:- 1.在定义匿名函数时就直接调用,这种方式匿名函数只能调用一次;- 2.将匿名函数赋给一个变量(该变量就是函数变量了),再通过该变量来调用匿名函数;- 如何让一个匿名函数,可以在整个程序中有效呢?将匿名函数给一个全局变量就可以了。

2 匿名函数案例

package mainimport "fmt"// 3.如果匿名函数是全局变量则可以被全局调用哟~
var mul = func(a, b int) int {return a * b
}func main() {var (x int = 7y     = 5)// 1.定义匿名函数,定义的同时调用sum := func(a int, b int) int {return a + b}(x, y)// 2.将匿名函数赋值给一个变量,这个变量实际就是函数类型的变量sub := func(a, b int) int {return a - b}result01 := sub(x, y)result02 := mul(x, y)fmt.Printf("sum = %d\n", sum)fmt.Printf("sub = %d\n", result01)fmt.Printf("mul = %d\n", result02)
}

三.闭包函数

1 闭包函数概述

- 什么是闭包函数:闭包就是一个函数和其他相关的引用环境组合的一个整体。- 闭包的本质:闭包本质依旧是一个匿名函数,只是这个函数引用外界的变量/参数,因此我们说: "匿名函数 + 引用外界的变量/参数 = 闭包"。- 闭包函数特点:- 1.返回的是一个匿名函数,但是这个匿名函数引用到函数外的变量/参数,因此这个匿名函数就和变量/参数形成一个整体,构成闭包;- 2.闭包中使用的变量/参数会一直保存在内存中,所以会一直使用,意味着闭包不可滥用;- 闭包的应用场景:闭包可以保留上次引用的某个值,我们传入一次就可以反复使用了。

2 闭包函数案例之返回上级函数内部变量

package mainimport ("fmt"
)// 闭包指的是一个函数和与其相关的引用环境组合而成的实体。
func getSum() func(int) int {// 此变量属于getSum函数var sum int = 0// 此处的闭包: "返回的是一个匿名函数+函数以外的变量sum"return func(x int) int {sum += xreturn sum}
}func getSum2(sum, x int) int {sum += xreturn sum
}func main() {// 不使用闭包的时候,想要保留sum值,但不可以反复使用,因此每次调用都需要重新传入sum的值fmt.Println(getSum2(0, 10))fmt.Println(getSum2(10, 20))fmt.Println(getSum2(30, 30))fmt.Println("----- 分割线 -----")// 定义了一个函数变量,闭包返回的匿名函数引用的那个变量会一直保存在内存中,可以一直使用。var f1 = getSum()// 闭包可以保留上次引用的某个值,我们传入一次就可以反复使用了。fmt.Println(f1(10))fmt.Println(f1(20))fmt.Println(f1(30))fmt.Println("----- 分割线 -----")// 此处重新定义了一个函数变量哟,注意,f2和上面的f1函数是两个独立的匿名函数哟~f2 := getSum()fmt.Println(f2(40))fmt.Println(f2(50))}

3 闭包函数案例之返回上级函数形参变量

package mainimport ("fmt"
)// 闭包函数不仅仅可以返回函数内部变量,也可以直接返回形参变量
func getSum(sum int) func(int) int {return func(x int) int {sum += xreturn sum}
}func main() {// 定义了一个函数变量,闭包返回的匿名函数引用的那个变量会一直保存在内存中,可以一直使用。var f1 = getSum(0)// 闭包可以保留上次引用的某个值,我们传入一次就可以反复使用了。fmt.Println(f1(10))fmt.Println(f1(20))fmt.Println(f1(30))}

4 闭包函数返回多个匿名函数案例

package mainimport ("fmt"
)func calc(base int) (func(int) int, func(int) int) {add := func(i int) int {base += ireturn base}sub := func(i int) int {base -= ireturn base}return add, sub
}func main() {// 闭包其实并不复杂,只要牢记闭包=函数+引用环境。f1, f2 := calc(10)fmt.Println(f1(1), f2(2))fmt.Println(f1(3), f2(4))fmt.Println(f1(5), f2(6))
}

5 闭包函数实现给文件加后缀

package mainimport ("fmt""strings"
)// 我们可以利用闭包函数,给文件加后缀
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("yinzhengjie"))fmt.Println(txtFunc("yinzhengjie"))
}

四.高阶函数

1 高阶函数概述

- 什么是高阶函数指的是: 一个函数的参数是函数,或者返回值是函数,满足其中一个就是高阶函数。闭包函数由于返回值是"匿名函数",因此我们说闭包也算得上是高阶函数的一个分支,但"高阶函数"不一定是匿名函数哟~- 高阶函数的应用场景:- 1.函数的参数或返回值可以使用函数,用于实现回调函数等功能;- 2.高阶函数可以用于实现递归函数,比如斐波拉契数列;- 3.高阶函数常常用于函数式编程,如列表的map,filter,reduce等函数; - 3.可用于实现柯力化函数,比如: 技术实现函数的复用和参数的延迟传递;- 4.部分应用(partial application),比如:将一个函数的参数固定下来,得到一个新的函数,继续调用;- 5.函数可以接受一个函数作为参数,然后根据这个函数的不同实现进行不同的处理;- 6.可以用函数组合技术将多个函数组合成一个新的函数;

2 高阶函数案例之函数作为参数案例

package mainimport ("fmt"
)func add(x, y int) int {return x + y
}func sub(x, y int) int {return x - y
}// 定义的形参中,要求传递的op变量是一个函数哟~
func calc(x, y int, op func(int, int) int) int {return op(x, y)
}func main() {var (a int = 100b int = 20)// 函数可以作为参数sum := calc(a, b, add)sub := calc(a, b, sub)fmt.Printf("%d + %d = %d\n", a, b, sum)fmt.Printf("%d - %d = %d\n", a, b, sub)
}

3 高阶函数案例之函数作为返回值

package mainimport ("errors""fmt"
)func add(x, y int) int {return x + y
}func sub(x, y int) int {return x - y
}// 函数也可以作为返回值
func do(s string) (func(int, int) int, error) {switch s {case "+":return add, nilcase "-":return sub, nildefault:err := errors.New("无法识别的操作符")return nil, err}
}func main() {var (a int = 100b int = 20)// 注意,此处返回的sum和sub都是函数哟~sum, _ := do("+")sub, _ := do("-")fmt.Printf("%d + %d = %d\n", a, b, sum(a, b)) // 调用sum函数fmt.Printf("%d - %d = %d\n", a, b, sub(a, b)) // 调用sub函数
}

五.defer关键字

1 defer执行时机

defer执行时机

- 什么是defer:- 1.Go语言中的"defer"语句会将其后面跟随的语句进行延迟处理;- 2.在defer归属的函数即将返回时,将延迟处理的语句按defer定义的逆序进行执行,先被defer的语句最后被执行,最后被defer的语句,最先被执行。- 3.在Go语言的函数中return语句在底层并不是原子操作,它分为给返回值赋值和RET指令两步。- defer的作用:在函数中,程序员经常需要创建资源,为了在函数执行完毕后,及时的释放资源,Go的设计者提供defer关键字。- defer的执行机制:defer语句执行的时机就在返回值赋值操作后,RET指令执行前。具体如上图所示。- defer应用场景:比如你想关闭某个使用的资源,因为defer有延迟执行机制(函数执行完毕再执行defer压入栈的语句),所以你用完随手写了关闭,比较省心,省事。

2 defer案例

package mainimport "fmt"func getSum(a, b int) (sum int) {/*1.在Golang中,程序遇到defer关键字,不会立即执行defer后的语句,而是将defer后的语句压入一个栈中,然后继续执行函数后面的代码;2.将defer语句压入栈中的同时,也会将相关的值同时拷贝到栈中,不会随着函数后面的变化而变化;3.栈的特点: 先进后出;4.在函数执行完毕以后,从栈中取出语句开始执行,按照先进后出的的规则执行语句;*/defer fmt.Printf("a = %d\n", a)defer fmt.Printf("b = %d\n", b)// 此处我们将a和b的值进行修改,但是并不会影响到defer语句中里的a和b变量对应的值哟~a += 100b += 200sum = a + bfmt.Printf("a = %d, b = %d, sum = %d\n", a, b, sum)return sum}func main() {fmt.Println(getSum(10, 20))
}

3 defer面试题

3.1 观察代码手写运算结果1

package mainimport ("fmt"
)func f1() int {x := 100defer func() {x++fmt.Println("in f1 x = ", x)}()return x
}func f2() (x int) {defer func() {x++fmt.Println("in f2 x = ", x)}()return 200
}func f3() (y int) {x := 300defer func() {x++fmt.Println("in f3 x = ", x)}()return x
}func f4() (x int) {defer func(x int) {x++fmt.Println("in f4 x = ", x)}(x)return 400
}func main() {fmt.Println(f1())fmt.Println(f2())fmt.Println(f3())fmt.Println(f4())
}

3.2 观察代码手写运算结果2

package mainimport ("fmt"
)func calc(index string, a, b int) int {ret := a + bfmt.Println(index, a, b, ret)return ret
}func main() {x := 1y := 2defer calc("AA", x, calc("A", x, y))x = 10defer calc("BB", x, calc("B", x, y))y = 20
}

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

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

相关文章

linux命令入门指南

linux命令入门指南 (个人学习所用,雷同之处请忽略) 关于linux 服务器种类物理服务器 需要机房(零度、无尘、无静电)--防止机器短路,数据丢失。(建造自己的机房或者进行托管) 自己的服务器需要维护。 云服务器 由服务器厂商提供(阿里云、腾讯云)--省钱、有人维护 vmwa…

基于FPGA的MSK调制解调系统verilog开发,包含testbench,同步模块,高斯信道模拟模块,误码率统计模块

1.算法仿真效果 本程序系统是《m基于FPGA的MSK调制解调系统verilog开发,并带FPGA误码检测模块和matlab仿真程序》的的升级。升级前原文链接增加了完整的AWGN信道模型的FPGA实现,可以在testbench里面设置SNR,分析不同SNR对应的FPGA误码率情况。vivado2019.2仿真结果如下(完整…

【总结】逻辑运算在Z3中运用+CTF习题

国际赛IrisCTF在前几天举办,遇到了一道有意思的题目,特来总结。题目并不是很难,没有复杂的ollvm混淆也没有复杂的加密,但是却一步一步引导我们去学习和总结。国际赛IrisCTF在前几天举办,遇到了一道有意思的题目,特来总结。题目附件如下:📎babyrevjohnson.tar 解题过程…

在Python中使用SWCNN去除水印

在Python中使用SWCNN去除水印 说明首次发表日期:2024-07-17 SWCNN Github官方仓库: https://github.com/hellloxiaotian/SWCNN SWCNN 论文链接: https://arxiv.org/abs/2403.05807准备 运行环境 首先创建一个conda环境,安装SWCNN官方建议的库: conda create -n py39torch …

Xilinx NVMe AXI4主机控制器,AXI4接口高性能版本介绍

NVMe AXI4 Host Controller IP可以连接高速存储PCIe SSD,无需CPU,自动加速处理所有的NVMe协议命令,具备独立的数据写入和读取AXI4接口,不但适用高性能、顺序访问的应用,也适用于随机访问的应用,同时结合外部存储器(比如DDR),使得Host端的数据访问管理更加灵活。NVMe A…

请问如何将带有斜纹水印pdf的转成Excel呢?

大家好,我是Python进阶者。 一、前言 前几天在Python最强王者交流群【wen】问了一个Python自动化办公的问题,问题如下:请问如何将带有斜纹水印pdf的转成Excel呢?目前我把pdf转成图片,根据水印的颜色进行清除,但是在脱网环境下无法将图片转成Excel。 二、实现过程 后来【隔…

并发问题的三大根源是什么?

本文探讨了在多线程环境下,CPU缓存、线程切换以及编译优化如何影响数据的可见性、原子性和有序性,并提出了相应的解决方案。在单核CPU时代,所有线程共享同一缓存,确保了数据的一致性。然而,多核CPU下,各核心拥有独立缓存,可能导致线程间数据更新不可见。1.前言 从进程与…

判断语句

判断语句 猜猜心里数字:n=6 if int(input("第一次输入数字"))==n:print("猜对了") elif int(input("第二次输入数字"))==n:print("第二次猜对了") elif int(input("第三次输入数字"))==n:print("第三次对了") el…

nacos RCE

1.漏洞原理 漏洞的核心在于 Nacos 的某些接口没有严格的权限控制,攻击者可以通过通过特定的 JSON 数据结构,远程执行恶意代码。以利用该漏洞读取敏感文件、执行系统命令。 条件:需要登录后才能利用漏洞 2.影响版本 nacos 2.3.2 nacos 2.4.0 3.环境搭建 https://github.com/a…

DataFountain-个贷违约预测实战

赛题来源:Datafountain 个贷违约预测 竞赛 - DataFountain 参考优秀选手方法单模走天下:公布一个单模型精度达0.9014(B榜第8)的算法 数据科学社区-DataFountain并加入自己理解整理如下:赛题理解 题目给出了train_internet.csv、train_public.csv、test_public.csv、submit_e…

数据仓库建模工具之一——Hive学习第三天

1、Hive的基本操作 1.1 Hive库操作 1.1.1 创建数据库1)创建一个数据库,数据库在HDFS上的默认存储路径是/hive/warehouse/*.db。create database testdb;2)避免要创建的数据库已经存在错误,增加if not exists判断。(标准写法)-- 中括号表示可以省略的内容 create database…