当前位置: 首页 > news >正文

golang学习笔记13-函数(二):init函数,匿名函数,闭包,defer

注:本人已有C,C++,Python基础,只写本人认为的重点。
这个知识点基本属于go的特性,比较重要,需要认真分析。

一、init函数

每个文件都可以定义init函数,它会在main函数执行前被调用,无论它的定义位置是在main后还是前。而全局变量的优先级又高于init,所以优先级是这样的:全局变量>init>main。示例如下:

package mainimport "fmt"var a = test()func test() int {fmt.Println("test已执行")return 1
}func init() {fmt.Println("init已执行")
}func main() {fmt.Println("main已执行")
}

上述程序的输出是:

test已执行
init已执行
main已执行

当多个文件存在init时,比如main所依赖的包中也有init,结果会怎样呢?假设main和依赖的包testutils内容如下:
main

package mainimport ("fmt""mod05/demo07/testutils"
)var a = test()func test() int {fmt.Println("test已执行")return 1
}func init() {fmt.Println("main中的init已执行")
}func main() {fmt.Println("main已执行")fmt.Println("age=", testutils.Age, "sex=",testutils.Sex, "name=", testutils.Name)
}

testutils

package testutilsimport "fmt"var Age int
var Sex string
var Name stringfunc init() {fmt.Println("testutils中的init已执行")Age, Sex, Name = 19, "女", "张三"
}

则程序运行结果为

testutils中的init已执行
test已执行
main中的init已执行
main已执行
age= 19 sex=name= 张三

显然,导入的包先执行,然后main中的test执行前先初始化全局变量,再执行test,最后执行init和main。所以顺序是:utils的全局变量->utils的init->main文件的全局变量>main文件的init->main文件的main函数。
总结下init的优先级:文件之间,被导包>当前包,文件内,全局变量>init>main

二、匿名函数

相对于C++和python的匿名函数,go的匿名函数就简单很多了,就是在函数定义前用一个变量接收,示例如下:

package mainimport "fmt"func main() {//定义匿名函数:定义的同时调用result := func(num1 int, num2 int) int {return num1 + num2}(10, 20)fmt.Println(result)//将匿名函数赋给一个变量,这个变量实际就是函数类型的变量//sub等价于匿名函数sub := func(num1 int, num2 int) int {return num1 - num2}//直接调用sub就是调用这个匿名函数了result01 := sub(30, 70)fmt.Println(result01)result02 := sub(30, 70)fmt.Println(result02)
}

需要注意的是,匿名函数定义后如果不用括号,那么这个变量就是匿名函数本身,如果用括号就是调用一次匿名函数,得到的是这个匿名函数的返回值,这个要好好理解,后面会有相关练习。

三、闭包(closure)

当函数返回一个匿名函数,且该匿名函数使用了它之外的变量,这个外部变量+该匿名函数就组成了一个闭包(closure),闭包形成后,该外部变量会一直留在内存中,示例如下:

package mainimport "fmt"func getSum() func(int) int {var sum int = 0            // 闭包中使用的变量return func(num int) int { // 函数中返回一个匿名函数sum = sum + num // 引用外部变量sumreturn sum}//返回的匿名函数+匿名函数以外的变量sum形成了闭包
}func main() {f := getSum()// 调用闭包fmt.Println(f(1)) //1fmt.Println(f(2)) //3fmt.Println(f(3)) //6fmt.Println(f(4)) //10// 这里,变量 sum 仍然存活,因为闭包仍然在使用它// 让闭包的引用消失f = nil // 现在没有任何引用指向 sum// 之后,如果没有其他地方引用 sum,它将被垃圾回收fmt.Println("----------------------")fmt.Println(getSum01(0, 1)) //1fmt.Println(getSum01(1, 2)) //3fmt.Println(getSum01(3, 3)) //6fmt.Println(getSum01(6, 4)) //10
}//不使用闭包的时候:我想保留的值,不可以反复使用
//闭包应用场景:闭包可以保留上次引用的某个值,我们传入一次就可以反复使用了
func getSum01(sum int, num int) int {sum = sum + numreturn sum
}

闭包进阶:匿名函数的闭包
练习1:分析以下几段代码,它们的输出分别是?(如果是地址就答地址即可)
代码1

func main() {counter := func() func() int {count := 0return func() int {count++return count}}()fmt.Println(counter())fmt.Println(counter())fmt.Println(counter())
}

代码2

func main() {counter := func() func() int {count := 0return func() int {count++return count}}fmt.Println(counter())fmt.Println(counter())fmt.Println(counter())
}

代码3

func main() {counter := func() func() int {count := 0return func() int {count++return count}}fmt.Println(counter()())fmt.Println(counter()())fmt.Println(counter()())
}

这个分析起来还是有一定难度的,留到文末讲,读者可先思考一会儿。

四、defer关键字

defer是go的一个关键字,用于推迟执行函数或函数调用语句,直到外层函数返回后再执行这个函数或函数调用语句。具体来说就是将当前函数或函数调用语句压入一个栈中,等外层函数执行完后,再按栈的顺序(后进先出,数据结构的内容)取出栈顶元素。注意,压入栈中时,函数或调用语句中的变量的值也会一起保存,属于值传递,示例如下:

package mainimport "fmt"func main() {res := func(num1 int, num2 int) int {defer fmt.Println("num1=", num1)defer fmt.Println("num2=", num2)num1 += 90num2 += 50return num1 + num2}(30, 60)fmt.Println("sum=", res)
}

显然,num1和num2的值在函数体开头就被保存到栈中了,所以程序输出如下:

num2= 60
num1= 30
sum= 230

OK,我们再来看前面的练习,其关键在于匿名闭包:

counter := func() func() int {count := 0return func() int {count++return count}
}()

首先,不用管返回值具体是什么,你得先搞清楚匿名函数的概念,之前说过:无括号,返回的就是匿名函数本身,有括号,就是匿名函数返回值。所以代码2和3就能做出来了:代码2调用了三次匿名函数,由于返回的是闭包,所以得到的是函数(闭包的本质就是匿名函数),将打印三次函数的地址(引用)。由于每次是重新调用匿名函数,所以是也就是刷新了三次闭包,得到了三个一样的闭包地址。代码3的counter也是函数,但调用语句多了括号,所以每次是重新调用匿名函数并调用闭包,所以是得到了三个一样的闭包的返回值,即三个1。理解了这两个,代码1就好理解了,有括号说明得到的是闭包,匿名函数就调用了这一次,所以之后操作的都是同一个闭包,并不会刷新。而闭包中的外部变量是一直存在的,所以结果是1 2 3。
到这里,如果你能完全理解,说明你对闭包和匿名函数的掌握到位了。这题呢,其实是本人和ChatGPT共同制作的一个题,因为我在问它闭包知识的时候,它给我的是代码1,然后我就在这基础上改了下并加以思考。这说明学习要多举一反三,多扩展一些情况,那么你对这个知识点的理解就比别人更深,这也是提高学习效率的方式之一,因为如果理解太浅,到后面就得补来补去,会浪费不少时间。

相关文章:

golang学习笔记13-函数(二):init函数,匿名函数,闭包,defer

注:本人已有C,C,Python基础,只写本人认为的重点。 这个知识点基本属于go的特性,比较重要,需要认真分析。 一、init函数 每个文件都可以定义init函数,它会在main函数执行前被调用,无论它的定义…...

HAproxy,nginx实现七层负载均衡

环境准备: 192.168.88.25 (client) 192.168.88.26 (HAproxy) 192.168.88.27 (web1) 192.168.88.28 (web2) 192.168.88.29 (php1) 192.168.88.30…...

ps aux | grep smart_webrtc这条指令代表什么意思

这条指令是在Linux系统中使用的命令,它的含义是列出所有正在运行的进程,并通过grep命令筛选出包含"smart_webrtc"关键字的进程。 具体解释如下: ps 是一个用于报告当前系统进程状态的命令。aux 是ps命令的选项,其中&a…...

第十三届蓝桥杯真题Python c组D.数位排序(持续更新)

博客主页:音符犹如代码系列专栏:蓝桥杯关注博主,后期持续更新系列文章如果有错误感谢请大家批评指出,及时修改感谢大家点赞👍收藏⭐评论✍ 问题描述 小蓝对一个数的数位之和很感兴趣, 今天他要按照数位之和给数排序。…...

【RabbitMQ】RabbitMq消息丢失、重复消费以及消费顺序性的解决方案

RabbitMq消息丢失主要是有三种情况:生产者消息未发送到服务端、服务端消息没有做持久化导致丢失、消费端未收到消息。解决方案依次如下: 开启事务或使用确认机制。对于一些重要的消息,生产者可以开启事务,确保消息发送成功后再提…...

海陆钻井自动化作业机器人比例阀放大器

海陆钻井自动化作业机器人是现代海洋石油勘探与钻井领域的关键装备,它通过自动化和无人化技术显著提高了钻井效率和安全性。海陆钻井自动化作业机器人主要用于在海上和陆地的钻井平台上进行自动化、无人化的一体化作业。这种设备能够自动切换钻杆,极大地…...

golang学习笔记19-面向对象(一):面向对象的引入

注:本人已有C,C,Python基础,只写本人认为的重点。 这节开始就是面向对象的内容了,为方便复用结构体等类型,本人定义了一个utils包,用于定义这些类型,之后的文章也会用到,希望读者注意…...

【从零开始实现stm32无刷电机FOC】【实践】【7.1/7 硬件设计】

目录 stm32电路磁编码器电路电机驱动电路电流采样电路电机选择本文示例硬件说明 为了承载和验证本文的FOC代码工程,本节设计了一个简易的三相无刷电机 硬件套件,主控采用非常常用的stm32f103c8t6单片机,电机编码器采用MT6701,电机…...

unix中父进程如何获取子进程的终止状态

一、前言 本文将介绍在unix系统中,父进程如何获取子进程的终止状态。本文主要围绕如下函数展开: 1.wait 2.waitpid 3.waitid 4.wait3、wait4 在讨论这些函数前,先介绍一个进程从创建到释放子进程的过程。 二、子进程的创建以及终止 在unix…...

【ESP 保姆级教程】小课设篇 —— 案例:20240505_基于esp01s的局域网控制灯

忘记过去,超越自己 ❤️ 博客主页 单片机菜鸟哥,一个野生非专业硬件IOT爱好者 ❤️❤️ 本篇创建记录 2024-09-30 ❤️❤️ 本篇更新记录 2023-09-30 ❤️🎉 欢迎关注 🔎点赞 👍收藏 ⭐️留言📝&#x1f64…...

Qt如何将外部窗口嵌入部件中

一、简述 今天给大家讲解的是使用QWindow类通过窗口句柄将外部的应用程序嵌入到我们的部件中来显示。在讲解之前可以延伸一下,当时项目中使用QProcess启动一些本地软件或者执行脚本时,需要将启动的第三方窗口嵌入到我们自己写的窗口中,此时我…...

2024年9月30日随笔

今天是国庆假期前的最后一天了,刚上完课,坐在实验室的工位前,感到焦虑又无奈,11月9号,网络规划工程师软考考试,学了一部分了,感觉有些难,还有一个月多一点点的时间,不知道…...

springboot+satoken实现刷新token(值变化)

欢迎来到我的博客,代码的世界里,每一行都是一个故事 🎏:你只管努力,剩下的交给时间 🏠 :小破站 springbootsatoken实现刷新token satoken是什么?支持什么?为什么需要&…...

63.HDMI显示器驱动设计与验证-彩条实验

(1)常见的视频传输接口有三种: VGA 接口、 DVI 接口和 HDMI 接口,目前的显示设备都配有这三种视频传输接口。三类视频接口的发展历程为 VGA→DVI→HDMI。其中 VGA 接口出现最早,只能传输模拟图像信号; 随后…...

安卓13设置删除网络和互联网选项 android13隐藏设置删除网络和互联网选项

总纲 android13 rom 开发总纲说明 文章目录 1.前言2.问题分析3.代码分析4.代码修改4.1修改方法14.2修改方法25.编译6.彩蛋1.前言 有些客户不想让用户修改默认的网络配置,禁止用户进入里面调整网络相关的配置。 2.问题分析 像这个问题,我们有好几种方法去处理,这种需求一般…...

C++的6种构造函数

在 C 中,构造函数是一种特殊的成员函数,用于初始化类对象。在对象创建时自动调用,构造函数的主要作用是分配资源、初始化数据成员等。根据不同的功能和使用场景,C 提供了多种类型的构造函数: 1. 默认构造函数 (Defaul…...

【FE】NPM——概述

NPM基本使用 下载Node 老生常谈&#xff0c;选择LTS版本官网放这里&#xff1a;https://nodejs.cn/download/ 1.镜像配置&#xff1a;镜像源 镜像配置 依赖仓库&#xff1a;版本查看 //不确定仓库有哪些版本&#xff0c;列出指定包的所有版本 npm view <package-name&…...

Clipboard.js实现复制文本到剪贴板功能

一、Clipboard.js简介 Clipboard.js是一个轻量级的实现复制文本到剪贴板功能的JavaScript插件&#xff0c;该插件可以将输入框&#xff0c;文本域&#xff0c;DOM节点元素中的文本内容复制到剪贴板中。 官网地址&#xff1a;Clipboard.js 浏览器兼容性&#xff1a;兼容Chrome、…...

Harbor安装笔记

下载离线安装包 wget https://github.com/goharbor/harbor/releases/download/v2.11.1/harbor-offline-installer-v2.11.1.tgz 解压 tar -zxvf harbor-offline-installer-v2.11.1.tgz 复制一份配置文件出来&#xff0c;修改配置 cp harbor.yml.tmpl harbor.yml vim harbor…...

HTTP 1.0 2.0 3.0详解

HTTP HTTP全称超文本传输协议&#xff0c;是一种属于应用层的通信协议。它允许将超文本标记语言文档&#xff08;HTML&#xff09;从Web服务器传输到客户端的浏览器。 HTTP报文结构 请求报文结构 请求方法&#xff1a; GET&#xff1a;一般用来请求已被URI识别的资源&#x…...

Python操作TXT文本:从入门到精通

在数字化时代,文本处理成为了许多工作和项目的基础。Python作为一种强大且易学的编程语言,在文本处理方面展现出了无与伦比的优势。本文将通过举例的方式,向读者介绍如何使用Python来操作TXT文本,让您轻松掌握文本处理的精髓。 一、读取TXT文本内容 首先,我们需要学会如…...

开源 AI 智能名片 2+1 链动模式 S2B2C 商城小程序的数据运营策略与价值创造

一、引言 1.1 研究背景 在当今数字化时代&#xff0c;数据运营已成为企业发展的核心驱动力。开源 AI 智能名片 21 链动模式 S2B2C 商城小程序作为一种创新的营销工具&#xff0c;与数据运营紧密相连。该小程序通过集成人工智能、大数据分析等先进技术&#xff0c;能够实时收集…...

ip 地址查看cmd命令

ip 地址查看cmd命令 在不同的操作系统中&#xff0c;查看IP地址的命令可能会有所不同。以下是一些常见操作系统中查看IP地址的命令&#xff1a; Windows: 打开命令提示符&#xff08;CMD&#xff09;&#xff0c;然后输入 ipconfig 命令。 Linux/Unix: 打开终端&#xff0…...

力扣9.26

931. 下降路径最小和 给你一个 n x n 的 方形 整数数组 matrix &#xff0c;请你找出并返回通过matrix 的下降路径 的 最小和 。 下降路径 可以从第一行中的任何元素开始&#xff0c;并从每一行中选择一个元素。在下一行选择的元素和当前行所选元素最多相隔一列&#xff08;即…...

HT8731 内置自适应H类升压和防破音功能的10W D类及AB类音频功率放大器

1、特点 防削顶失真功能(防破音,Anti-Clipping Function, ACF) 免滤波器数字调制&#xff0c;直接驱动扬声器 输出功率 10W(VBAT4.2V,RL3Ω,THDN10%, fiN 1kHz) 6W(VBAT3.3~4.2V,RL4Ω,THDN<1%,20-20kHz 全频段) 3W (VBAT3.3~4.2V,RL8Ω, THDN<1%, 20- 20kHz 全频段 VB…...

webpack使用

一、简介 概述 本次使用webpack4进行构建打包 二、webpack 安装webpack、webpack-cli npm install webpack4.2.0 webpack-cli4.2.0 -D 三、loader 加载器概述 raw-loader&#xff1a;加载文件原始内容&#xff08;utf-8&#xff09; file-loader&#xff1a;把文件输出…...

高通Android 12 音量API设置相关代码

// 获取当前音量大小public static int getCurrentVolume(Context context) {AudioManager audioManager (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);return audioManager.getStreamVolume(AudioManager.STREAM_MUSIC); // 使用 STREAM_MUSIC 作为示例…...

Qt开发第一讲

一、Qt项目里面有什么&#xff1f; 对各个文件的解释&#xff1a; Empty.pro文件 QT core gui # 要引入的Qt模块&#xff0c;后面学习到一些内容的时候可能会修改这里 #这个文件相当于Linux里面的makefile文件。makefile其实是一个非常古老的技术了。 #qmake搭配.pr…...

详细指南:如何有效解决Windows系统中msvcp140.dll丢失的解决方法

如果你在使用Windows系统时遇到“msvcp140.dll丢失”的错误提示&#xff0c;通常是因为你的计算机上缺少或损坏了msvcp140.dll文件。msvcp140.dll是Microsoft Visual C Redistributable包的一部分&#xff0c;许多应用程序和游戏需要它来正常运行。以下是几种解决msvcp140.dll丢…...

【RabbitMQ】幂等性、顺序性

幂等性 概述 幂等性是数学和计算机科学中某些运算的性质&#xff0c;他们可以被多次应用&#xff0c;而不会改变初始应用的结果。RabbitMQ的幂等性则是指同一条消息&#xff0c;多次消费&#xff0c;对系统的影响是相同的。 一般消息中间件的消息传输保障分为三个层级&#…...

工信部网站备案系统/网站推广的渠道有

冯.诺依曼体系结构,个人的理解:物理电学补充:所有的物质,是由分子或原子组成的。分子是能保持物质化学性质不变的最小微粒。分子是由原子组成的&#xff0c;可分为单原子分子和多原子分子。原子的原子核式结构&#xff1a;原子的中心为原子核&#xff0c;电子在不同轨道上绕着原…...

建设银行办信用卡网站/网络推广公司专业网络

发这篇心得主要想告诉大家博主的博客虽然是随心情发的&#xff0c;但博主真的没有偷懒&#xff0c;一直在学习&#xff01;而且对现在的博主来说专业技能固然重要&#xff0c;但在未来规划还没定之前&#xff0c;广度还是更重要的&#xff01;对的&#xff0c;我要摊牌&#xf…...

甘孜网站建设/东莞网站推广软件

转载于:https://www.cnblogs.com/xiaobiaomei/p/9216717.html...

深圳网站建设公/网络推广方案设计

G代码是数控程序中的指令。一般都称为G指令。1. 代码功能代码名称-功能简述G00------快速定位G01------直线插补G02------顺时针方向圆弧插补G03------逆时针方向圆弧插补G04------定时暂停G05------通过中间点圆弧插补G06------抛物线插补G07------Z 样条曲线插补G08------进给…...

苹果手机如何做微电影网站/游戏优化大师下载安装

vxe-table官网...

怎么做彩票网站的代理/新闻媒体发布平台

要开始我是非常新的自举&#xff0c;但我想要做一些类似于这个例子&#xff1a;text over background image但在一个引导框架内。我曾尝试在导航栏下面的容器内创建一个div&#xff0c;但似乎在背后的文字背后放置了白色背景......您可以在此处看到我的代码&#xff1a;JSFiddl…...