GoLong的学习之路(十)语法之函数
书接上回,上回书说到,结构体,一言之重在于体。一体之重在于经。经之重甚于骨。这张就说go的经络—函数。
文章目录
- 函数
- 函数如何定义
- 参数
- 可变参数
- 返回值
- 多返回值
- 函数类型与变量
- 高阶函数
- 函数作为参数
- 函数作为返回值
- 匿名函数
- 闭包
- defer语句
- 底层逻辑
- 内置函数
- panic/recover(错误处理)
函数
函数是组织好的、可重复使用的、用于执行指定任务的代码块。
Go语言中支持函数、匿名函数和闭包。
函数如何定义
Go语言中定义函数使用func关键字(其实吧,我认为这个里面的所有都是要加func,当然结构体不加)
func 函数名(参数)(返回值){函数体
}
注意
- 函数名:由字母、数字、下划线组成。
- 函数名的第一个字母不能是数字
- 在同一个包内,函数名也称不能重名
- 参数:参数由参数变量和参数变量的类型组成,多个参数之间使用,分隔。
- 返回值:返回值由返回值变量和其变量类型组成,也可以只写返回值的类型,
多个返回值
必须用()包裹,并用,分隔。(这个就有点牛了) - 函数体:实现指定功能的代码块。
函数的参数和返回值都是可选的。
func sayHello() {fmt.Println("Hello")
}
定义了函数之后,我们可以通过函数名()的方式调用函数。 调用有返回值的函数时,可以不接收其返回值。
func main() {sayHello()
}
参数
函数的参数中如果相邻变量的类型相同,则可以省略类型
func intSum(x, y int) int {return x + y
}
可变参数
可变参数是指函数的参数数量不固定。Go语言中的可变参数通过在参数名后加…来标识。这种方式在java中是通过重写来实现。但是这里似乎就不许要这么麻烦。
注意:可变参数通常要作为函数的最后一个参数。
func intSum2(x ...int) int {fmt.Println(x) //x是一个切片sum := 0for _, v := range x {sum = sum + v}return sum
}
ret1 := intSum2()
ret2 := intSum2(10)
ret3 := intSum2(10, 20)
ret4 := intSum2(10, 20, 30)
fmt.Println(ret1, ret2, ret3, ret4) //0 10 30 60
注意
固定参数搭配可变参数使用时,可变参数要放在固定参数的后面
实际上:函数的可变参数是通过切片来实现的。
返回值
Go语言中通过return关键字向外输出返回值。其实这个大差不差,毕竟语言差别都差不多
多返回值
Go语言中函数支持多返回值,函数如果有多个返回值时必须用()将所有返回值包裹起来
func calc(x, y int) (int, int) {sum := x + ysub := x - yreturn sum, sub
}
其实这个括号的返回参数的类型,可以为任意属性,但是在return的返回值时,也必须按照这个返回顺序返回。
当我们的一个函数返回值类型为slice时,nil可以看做是一个有效的slice,没必要显示返回一个长度为0的切片。
func someFunc(x string) []int {if x == "" {return nil // 没必要返回[]int{}}...
}
函数定义时可以给返回值命名,并在函数体中直接使用这些变量,最后通过return关键字返回
func calc(x, y int) (sum, sub int) {sum = x + ysub = x - yreturn
}
函数类型与变量
我们可以使用type关键字来定义一个函数类型:
type calculation func(int, int) int
定义了一个calculation类型,它是一种函数类型,这种函数接收两个int类型的参数并且返回一个int类型的返回值。
凡是满足这个条件的函数都是calculation类型的函数。
type calculation func(int, int) intfunc add(x, y int) int {return x + y
}func sub(x, y int) int {return x - y
}var c calculationfunc main() {c = addfmt.Println(c(10, 20))c = subfmt.Println(c(10, 20))
}
高阶函数
高阶函数分为函数作为参数
和函数作为返回值
这两部分两部分。
函数作为参数
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, nilcase "-":return sub, nildefault:err := errors.New("无法识别的操作符")return nil, err}
}
匿名函数
当函数作为返回值时,在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 intreturn func(y int) int {x += yreturn x}
}func main() {var f = adder()fmt.Println(f(10)) //10+0fmt.Println(f(20)) //30 = 10 + 20fmt.Println(f(50)) //80 = 50 + 30f2 := adder()fmt.Println(f2(60)) //60 = 0 + 60fmt.Println(f2(30)) //90 = 30 + 60
}
变量f是一个函数并且它引用了其外部作用域中的x变量,此时f就是一个闭包。 在main方法中f
成为了adder
函数的指针,此时,他作为一个函数,x 是 属于第二个函数的全局变量。在这种情况下,在 f
的生命周期没有结束,此时x中的有效值就会一直存在。
闭包其实并不复杂,只要牢记闭包=函数+引用环境
当然在结合上面普通的函数特点我们就可以将闭包升级。
在函数中添加参数
func adder2(x int) func(int) int {return func(y int) int {x += yreturn x}
}
在匿名函数中做逻辑判断
func makeSuffixFunc(suffix string) func(string) string {return func(name string) string {if !strings.HasSuffix(name, suffix) {return name + suffix}return name}
}
返回多个函数
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
}
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")
}
这就最先执行defer,最后执行的意思
底层逻辑
在Go语言的函数中return语句在底层并不是原子操作,它分为给返回值赋值和RET指令两步。
而 defer
执行的时机就在返回值赋值操作后。
看一道题:
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
}
想一想打印什么?
发现没有,在7,8中有一个问题,就是x的值,此时的8的x的值是之前没有变成 10 之前的。
内置函数
内置函数 | 介绍 |
---|---|
close | 主要用来关闭channel |
len | 用来求长度,比如string、array、slice、map、channel |
new | 用来分配内存,主要用来分配值类型,比如int、struct。返回的是指针 |
make | 用来分配内存,主要用来分配引用类型,比如chan、map、slice |
append | 用来追加元素到数组、slice中 |
panic和recover | 用来做错误处理 |
panic/recover(错误处理)
Go语言中目前是没有异常机制,但是使用panic/recover模式来处理错误。panic可以在任何地点引发,但recover只有在defer调用的函数中有效。
func funcA() {fmt.Println("func A")
}func funcB() {panic("panic in B")
}func funcC() {fmt.Println("func C")
}
func main() {funcA()funcB()funcC()
}
程序运行期间funcB中引发了panic导致程序崩溃,异常退出了。但是我们要如何像java中那样去抛出异常呢?
这个时候我们就可以通过recover将程序恢复回来,继续往后执行
func funcA() {fmt.Println("func A")
}func funcB() {defer func() {err := recover()//如果程序出出现了panic错误,可以通过recover恢复过来if err != nil {fmt.Println("recover in B")}}()panic("panic in B")
}func funcC() {fmt.Println("func C")
}
func main() {funcA()funcB()funcC()
}
注意
:这个就有一点像Java的 try…catch
- recover()必须搭配defer使用。
- defer一定要在可能引发panic的语句之前定义。
相关文章:

GoLong的学习之路(十)语法之函数
书接上回,上回书说到,结构体,一言之重在于体。一体之重在于经。经之重甚于骨。这张就说go的经络—函数。 文章目录 函数函数如何定义参数可变参数 返回值多返回值 函数类型与变量 高阶函数函数作为参数函数作为返回值匿名函数闭包defer语句底…...

随笔:使用Python爬取知乎上相关问题的所有回答
项目中数据分析的需要自己从知乎某个专门的问题上爬数据,但众所周知,知乎的问题的显示方式有点胃疼(指滑动后下翻加载更多回答,还经常卡住),翻了翻网上的教程发现有的要么就是很老了要么就是付费的…...

ORB-SLAM安装过程遇到问题记录整理
一、ORB-SLAM2 1.c error: ‘decay_t’ is not a member of ‘std’ 如下图所示: 解决方法: 修改 ORB_SLAM的 CMAKELIST.txt文件, 将set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdc11") 修改为 set(CMAKE_CXX_STANDARD 14) 2…...
Ubuntu22.0.4安装svn服务
1、检查是否已安装 1.1、检查是否已安装 svnserve --version1.2、删除SVN遗留文件 sudo apt-get remove --purge subversion2、安装svn apt-get install subversion3、新建存储目录 sudo mkdir /data/svn sudo mkdir /data/svn/repository4、更改文件夹的读写权限 sudo…...
GNSS边坡位移监测仪在自然灾害应急能力提升工程领域的应用
GNSS边坡位移监测仪在自然灾害应急能力提升工程领域的应用 二、工作原理 GNSS的基本原理是测量出已知位置的卫星到用户接收机之间的距离,然后综合多颗卫星的数据就可知道接收机的具体位置。要达到这一目的,卫星的位置可以根据星载时钟所记录的时间在卫星…...

k8s客户端配置
K8s客户端安装 前提 K8s服务部署成功,如下 角色 IP地址 操作系统 主机名 Kubernetes版本 master节点 172.16.4.167 CentOS 7.9 k8s-master01 v1.28.2 工作节点1 172.16.4.168 CentOS 7.9 k8s-worker01 v1.28.2 工作节点2 172.16.4.169 CentOS 7.9…...

网络套接字编程
1.基础预备知识 1.1源ip和目的ip 在IP数据包头部中, 有两个IP地址, 分别叫做源IP地址, 和目的IP地址 源IP地址表示发起通信的设备的IP地址。它是数据包的出发点,标识了数据包的来源。当一个设备发送数据包到网络上的其他设备时,该数据包的源IP字段会被…...

Node编写更新用户信息接口
目录 前言 定义路由和处理函数 验证表单数据 实现更新用户基本信息的功能 前言 继前面几篇文章,本文介绍如何编写更新用户信息接口 定义路由和处理函数 路由 // 更新用户信息接口 router.post(/userinfo, userinfo_handler.updateUserinfo) 处理函数 // 导…...
Delphi解决 openssl DLL 与 Indy 的SSL/TLS 连接问题
昨天,突然间,我的一个 Delphi 程序无法连接到互联网上的各种WMS服务器。我收到以下错误消息: 使用 SSL 连接时出错。错误 1409442E:SSL 例程:ssl3_read_bytes:tlsv1 警报协议版本 由于我使用的是最新版本…...

单片机仿真设计打包项目
小伙伴们在仿真设计时会遇到各种各样的问题,网上的资料可能不全或者很贵。 这篇也不单纯为了打广告,主要是希望实实在在帮到学单片机的同学,大家不要一有问题就各种找dai zuo,做的好不好是一回事儿,关键是它费&#x…...

Java练习题-输出二维数组对角线元素和
✅作者简介:CSDN内容合伙人、阿里云专家博主、51CTO专家博主、新星计划第三季python赛道Top1🏆 📃个人主页:hacker707的csdn博客 🔥系列专栏:Java练习题 💬个人格言:不断的翻越一座又…...
Python调用ctype的动态库时出现的问题记录
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、动态库调用问题1.问题发现2.解决问题 总结 前言 提示:这里可以添加本文要记录的大概内容: 之前用公司算法同事写的c算法编译成的d…...

面试算法38:每日温度
题目 输入一个数组,它的每个数字是某天的温度。请计算每天需要等几天才会出现更高的温度。例如,如果输入数组[35,31,33,36,34],那么输出为[3,1,1,0ÿ…...
流程控制语句中的顺序结构、分支结构和循环结构以及示例和详细代码解释为什么这样写(1)
在流程控制语句中,我们通常可以将其分为三种基本结构:顺序结构、分支结构和循环结构。 1. 顺序结构:顺序结构是最简单的流程控制结构,代码按照编写的顺序依次执行,没有条件或循环的干扰。下面是一个顺序结构的示例代码…...
MFC Windows 程序设计[334]之自定义编辑框(附源码)
MFC Windows 程序设计[334]之自定义编辑框 程序之美前言主体运行效果核心代码逻辑分析结束语程序之美 前言 MFC是微软公司提供的一个类库(class libraries),以C++类的形式封装了Windows API,并且包含一个应用程序框架,以减少应用程序开发人员的工作量。其中包含大量Wind…...

MOS管特性及其几种常用驱动电路详解,电子工程师手把手教你
在电子工程中,MOS管(金属氧化物半导体场效应管)是一种非常重要的半导体元件。 在这篇文章中,我们将深入探讨MOS管的特性,以及几种常用的驱动电路的工作原理和设计方法。无论你是初学者还是经验丰富的电子工程师&#…...

C#,数值计算——分类与推理Phylo_wpgma的计算方法与源程序
1 文本格式 using System; using System.Collections.Generic; namespace Legalsoft.Truffer { public class Phylo_wpgma : Phylagglom { public override void premin(double[,] d, int[] nextp) { } public override double dminfn(double…...
Spring MVC 常用的注解
Controller:用于将一个类标记为 Spring MVC 控制器,处理 HTTP 请求和生成 HTTP 响应。RestController:类似于 Controller,但返回的数据会被自动转换为 JSON 或 XML 格式,通常用于构建 RESTful API。等于Controller Re…...

winodos下使用VS2022编译eclipse-paho.mqtt.c并演示简单使用的 demo
本文演示C语言如何使用eclipse-paho.mqtt.c库,包含自行编译库的步骤或者下载编译好的文件。 1.下载paho.mqtt.c库源码(zip 文件) 到官网选择C版本的paho源码进行下载 Eclipse Paho | The Eclipse Foundation 或者到下述连接下载 Releases ec…...

【Java 进阶篇】使用 Java 和 Jsoup 进行 XML 处理
XML(可扩展标记语言)是一种常用的数据交换格式,它被广泛用于在不同系统之间传递和存储数据。Java作为一种强大的编程语言,提供了多种方式来处理XML数据。其中,Jsoup 是一个流行的Java库,用于解析和操作XML文…...
在鸿蒙HarmonyOS 5中实现抖音风格的点赞功能
下面我将详细介绍如何使用HarmonyOS SDK在HarmonyOS 5中实现类似抖音的点赞功能,包括动画效果、数据同步和交互优化。 1. 基础点赞功能实现 1.1 创建数据模型 // VideoModel.ets export class VideoModel {id: string "";title: string ""…...

el-switch文字内置
el-switch文字内置 效果 vue <div style"color:#ffffff;font-size:14px;float:left;margin-bottom:5px;margin-right:5px;">自动加载</div> <el-switch v-model"value" active-color"#3E99FB" inactive-color"#DCDFE6"…...
LLM基础1_语言模型如何处理文本
基于GitHub项目:https://github.com/datawhalechina/llms-from-scratch-cn 工具介绍 tiktoken:OpenAI开发的专业"分词器" torch:Facebook开发的强力计算引擎,相当于超级计算器 理解词嵌入:给词语画"…...

【JavaWeb】Docker项目部署
引言 之前学习了Linux操作系统的常见命令,在Linux上安装软件,以及如何在Linux上部署一个单体项目,大多数同学都会有相同的感受,那就是麻烦。 核心体现在三点: 命令太多了,记不住 软件安装包名字复杂&…...

零基础在实践中学习网络安全-皮卡丘靶场(第九期-Unsafe Fileupload模块)(yakit方式)
本期内容并不是很难,相信大家会学的很愉快,当然对于有后端基础的朋友来说,本期内容更加容易了解,当然没有基础的也别担心,本期内容会详细解释有关内容 本期用到的软件:yakit(因为经过之前好多期…...

微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据
微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据 Power Query 具有大量专门帮助您清理和准备数据以供分析的功能。 您将了解如何简化复杂模型、更改数据类型、重命名对象和透视数据。 您还将了解如何分析列,以便知晓哪些列包含有价值的数据,…...
Web中间件--tomcat学习
Web中间件–tomcat Java虚拟机详解 什么是JAVA虚拟机 Java虚拟机是一个抽象的计算机,它可以执行Java字节码。Java虚拟机是Java平台的一部分,Java平台由Java语言、Java API和Java虚拟机组成。Java虚拟机的主要作用是将Java字节码转换为机器代码&#x…...
MySQL 部分重点知识篇
一、数据库对象 1. 主键 定义 :主键是用于唯一标识表中每一行记录的字段或字段组合。它具有唯一性和非空性特点。 作用 :确保数据的完整性,便于数据的查询和管理。 示例 :在学生信息表中,学号可以作为主键ÿ…...

华为OD机试-最短木板长度-二分法(A卷,100分)
此题是一个最大化最小值的典型例题, 因为搜索范围是有界的,上界最大木板长度补充的全部木料长度,下界最小木板长度; 即left0,right10^6; 我们可以设置一个候选值x(mid),将木板的长度全部都补充到x,如果成功…...
前端高频面试题2:浏览器/计算机网络
本专栏相关链接 前端高频面试题1:HTML/CSS 前端高频面试题2:浏览器/计算机网络 前端高频面试题3:JavaScript 1.什么是强缓存、协商缓存? 强缓存: 当浏览器请求资源时,首先检查本地缓存是否命中。如果命…...