Go——函数

news2024/11/18 4:26:18

一. 函数定义

        1.1 特点

  • 无需声明原型
  • 支持不定变参
  • 支持多返回值
  • 支持命名返回参数
  • 支持匿名函数和闭包
  • 函数也是一种类型,一种函数可以赋值给变量
  • 不支持嵌套,一个包不能有两个名字一样的函数
  • 不支持重载
  • 不支持默认参数

        1.2 函数声明

        函数声明包含一个函数名,参数列表,返回值列表和函数体。如果没有返回值,则返回值列表可以省略。函数从第一条语句开始执行,直到执行return语句或者函数体的最后一条语句。

        函数可以没有参数或者接受多个参数。注意:类型在变量名后。

        当两个或者两个以上的函数命名参数是同一类型,则除最后一个类型之外,其它可以省略。

        函数可以返回任意数量的返回值。

        使用关键字func定义函数,左括号不能另起一行。

func test(s string, x, y int) (int, string) {
	n := x + y
	return n, fmt.Sprintf(s, n)
}

        函数是第一类对象,可作为参数传递。建议将复杂签名定义为函数类型,以便于阅读。 

        有返回值的函数,必须有明确的终止语句,否则会引发编译错误。

        你可能会偶尔遇到没有函数体的函数声明,这表示该函数不是以Go实现的。这样的声明定义了函数标识符。

package math

func Sin(x float64) float //用汇编语言实现

        1.3 参数

        函数定义时指出,函数定义时有参数,该变量可以称为函数形参。形参就像定义在函数体内的局部变量。

        但调用函数时,传递过来的变量就是函数的实参。函数可以通过两种方式来传递参数:

        值传递:指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响实际参数。

        引用传递:是指在调用函数时将实际参数的地址传递到函数中,那么在函数中对参数进行修改,将影响实际参数。

        在默认情况下,Go语言是值传递,即在调用过程中,不会影响实际参数。

        注意:

  • 无论是值传递还是引用传递,传递给函数的都是变量的副本。不过,值传递是值的拷贝,引用传递是指针的拷贝(解引用后就是实际的值)。
  • 一般来说,地址拷贝更加高效(指针在32位下占4字节,在64位下占8字节)。而值拷贝取决于拷贝对象的大小,对象越大性能越低。
  • map,slice,chan,指针,interface默认以引用传递

不定参数传递:

        不定参数传值,就是函数的参数不是固定的,后面的类型是固定的。(可变参数)

        Golang可变参数的本质上就是一个切片slice。只能有一个,且必须是最后一个。

        在参数赋值时,可以不用一个个的赋值,可以直接传递一个切片。注意传递切片时在可变参数后需要加"..."。

func myFunc1(args ...int) {//0个或多个参数

}

func myFunc2(a int, args ...int){//1个或多个参数

}

func myFunc3(a int, b int, args ...int){ //2个或多个参数

}

注意:其中args是一个slice,我们可以通过arg[index]依次访问所有的参数,通过len(args)来判断参数的个数。

任意类型的不定参数: 

         任意类型的不定参数,就是函数参数个数和每个参数的类型都是不固定的。

        用户interface{}传递任意类型数据是Go语言的常用惯例,而且interface{}是类型安全的。

func myFunc(args ...interface{}) {

}

        1.4 返回值 

特点

  • "_"标识符,用来忽略函数的返回值
  • Go的返回值可以被命名,并且就像函数开头声明的变量那样使用
  • 返回值的名称应当具有一定的意义,可以作为文档使用
  • 没有参数的return语句返回各个返回变量的当前值(用在返回值有名称的情况)。这种用法被称作"裸"返回
  • 直接返回语句仅应当用在短函数中。在长函数中它们会影响代码的可读性。
  • 命名返回值参数可看做与形参类似的局部变量,最后由return隐式返回 

  • Golang返回值不能用容器对象接收多返回值。只能用多个变量,或者"_"忽略返回值。 即有几个返回值,就需要几个变量接收

  • 多返回值可以直接作为其它函数调用的实参 

 

  • 命名返回参数可被同名局部变量遮掩,此时需要显示返回

  • 命名返回参数允许defer延迟调用通过闭包读取和修改 

  • 显示return返回前,会先修改命名返回参数 

        1.5 匿名函数

        匿名函数是指不需要定义函数名的一种函数实现方式。 

        在Go语言中,函数可以像普通变量一样被传递或使用,Go语言支持随时在代码里定义匿名函数。

        匿名函数由一个不带函数名的函数声明和函数体组成。匿名函数的优点在于可以直接使用函数内的变量,不必声明。

         对比C/C++,函数虽然也可以作为变量或参数,但是函数需要有名字。

        Golang匿名函数可以赋值给变量,作为结构体字段,或者在channel里传送。

        1.6 闭包

        闭包是由函数及其相关引用环境组合而成的实体(即:闭包=函数+引用环境)。

        官方的解释是:所谓闭包,指的是一个拥有许多变量和绑定了这些变量的环境表达式,通常是一个函数,因而这些变量也是表达式的一部分。

        维基百科讲,闭包是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。所以,由另外一种说法认为闭包是由函数和与其相关的引用环境组合而成的实体。闭包在运行时可以有很多实例,不同的引用环境和相同的函数组合可以参数不同的实例。

        看着上面的描述,会发现闭包和匿名函数很像。下面来一个例子。

  • 定义 

         函数b嵌套在函数a内部,函数a返回函数b,这样执行完var c=a()后,实际c指向了函数b(),再执行c()会执行函数b打印变量i,第一次为1,第二次为2,第三次为3,以此类推。其实,这段代码就创建了一个闭包。因为函数a()外的变量c引用了函数a()内的函数b()。也就是说:当函数a()的内部函数b()被函数a()外的一个变量引用的时候,就创建了一个闭包。

        在上面例子中,由于闭包的存在使得函数a返回后,a中的变量i始终存在,这样每次执行c(),i都是自加1后的值。闭包使得Go的垃圾回收机制GC不会回收a()占用的资源。

  • 作用

        在给定函数被多次调用的过程中,这些私有变量能够保持其持久性但是变量的作用域仅限于包含它们的函数,所以在其它程序代码中无法进行访问。变量的生存周期变长,在一次函数调用期间所创建生成的值在下次函数调用时仍然存在(所以上面打印的值为1,2,3)。正因为上面的特点,闭包可以用来完成信息隐藏,并进而应用于需要状态表达的某些编程范型中。

        对于上面的例子,如果函数a返回的不是函数b,情况就完全不同了。因为函数a执行完后,函数b没有被返回给函数a的外界,只是被函数a引用了,函数a被函数b引用。所以函数a和函数b互相引用不被外界引用。函数a和b会被垃圾回收机制GC回收。

  • 引用环境

        上面的例子,c()跟c2()引用的是不同的环境,在调用i++时修改的不是同一个i,因此开始时输出的都是1。函数a()每进入一次,就形成了新的环境,对应的闭包中,函数都是同一个函数,环境确是引用不同的环境。这和c()和c1()的调用顺序无关。

        闭包进入一次,引用环境不同。

  • 闭包复制的是原对象指针,这就很容易解释延时引用现象

        匿名函数变量i和test()函数变量i是同一个。

        在汇编层,test实际返回的是FuncVal对象,其中包含匿名函数地址,闭包对象指针。当调用匿名函数时,只需以某个寄存器传递该对象即可。

FuncVal { func_address, closure_var_pointer ... }
  •  外部引用函数参数的局部变量

  • 返回两个闭包

         1.7 递归

        递归,就是在运行的过程中调用自己。一个函数调用自己,就叫做递归函数。

        构成递归需具备的条件:

  1. 子问题须与原始问题为同样的事情,且更为简单
  2. 不能无限制地调用本身,必须有个出口,化简为非递归状况处理
  • 数字阶乘

        一个正整数的阶乘是所有小于等于该数地正整数地积,并且0的阶乘为1。

package main

import "fmt"

func factorial(x int) int {
	if x <= 1 {
		return 1
	}

	return x * factorial(x-1) //自己调用自己
}

func main() {
	res := factorial(5)
	fmt.Println(res)
}
  • 斐波那契数列

        1.8 延时调用(defer)

        defer特性:

  • 关键字defer用于注册延时调用
  • 这些调用直到return前才被执行。因此,可以用来做资源清理
  • 多个defer语句,按先进后出的方式执行。因为后面的语句会依赖前面的资源,因此如果前面的资源释放了,后面的语句就没法执行了。
  • defer语句中的变量,在defer声明前就决定了。

        defer用途:

  • 关闭文件句柄
  • 锁资源释放
  • 数据库连接释放

        defer触发时机:

  • 包裹着defer语句的函数返回时
  • 包裹着defer语句的函数执行完时
  • 当前goroutine发送Panic时

        实例:

        defer先进后出:

  • 当go遇到一个defer语句时,不会立即执行,而是将defer后面的语句压入到一个栈中,然后继续执行函数下的语句。
  • 当函数或方法执行完毕,再从栈中依次从栈顶取出执行(先入后出)。

         i是从0到4,但是由于defer先进后出,所以是4到0。

        defer遇上闭包:

  • 在Go语言中,对外部作用域中变量访问的方式是"引用",捕获的是变量的地址。当外部变量发生改变时,闭包中的变量也会发生改变。

  • 多个defer语句注册,按先入后出次序执行。哪怕函数或某个延时调用发生错误,这些调用依旧会被执行。 

  •  延迟调用参数在注册时求值或复制,可以用指针或闭包"延时读取"

  • 滥用defer可能导致性能问题,尤其是在一个大循环里面 

defer陷阱: 

  • defer与闭包:

        由于闭包使用的外部变量保存的是地址。修改外部变量可以得到最新的值。

  • defer与return

  • defer nil函数 

        值得注意的是run在声明是不会报错,而是在使用是报错。

  •  在错误位置使用defer

        当http.Get失败时会抛异常。

        修改:

        获取返回错误:

  • 释放相同资源 

         解决方案:不建立闭包,将变量传进来,这样是值拷贝。

         1.9 异常处理

        Golang没有结构化异常,使用panic抛出错误,recover捕获错误。

        使用场景:Go中可以抛出一个panic异常,然后在defer中通过recover捕获这个异常,然后正常处理。

        panic:

  • 内置函数
  • 假如函数F中书写了panic语句,会终止其后要执行的代码,在panic所在函数F内如果存在要执行的defer函数列表,按照defer的逆序执行
  • 返回函数F的调用者G,在G中,调用函数F语句之后的代码不会执行,假如函数G中存在要执行的defer函数列表,按照defer的逆序执行
  • 直到goroutine整个退出,并报告错误

        recover:

  • 内置函数
  • 用来控制一个goroutine的panicking行为,捕获panic,从而影响应用的行为。 
  • 一般的调用建议:
    • 在defer函数中,通过recever来终止一个goroutine的panicking过程,从而恢复正常代码的执行。
    • 可以通过panic传递错误

        注意:

  • 利用recover处理panic指令,defer必须放在panic之前定义,另外recover只有在defer调用函数中才有效。否则当panic时,recover无法捕获到panic,无法防止panic扩散。
  • recover处理异常后,逻辑并不会恢复到panic那个点去,函数跑到defer之后的那个点
  • 多个defer会形成defer栈,后定义的defer语句会被最先调用

 由于panic和recover参数类型为interface{},因此可以抛出任何类型的对象

func panic(v interface{})
func recover()interface{}
  •  向已关闭的通道发送数据会引发panic

  • 延时调用中引发错误,可被后续延迟调用捕获,但仅最后一个错误可被捕获

 

  • 当异常panic被回收recover后,就没了,后面的recover回收不到该异常

  • 捕获函数recover只有在延时调用内直接调用才会终止错误,否则捕获不到异常(返回nil),任何未捕获的的错误都会沿调用堆栈向外传递。

         也可以不使用匿名函数,而是使用函数,也可以回收异常。

  • 如果需要保护代码段,可将代码重构成匿名函数,如此可确保后续代码被执行

  • 除了用panic来引发中断性错误外,还可返回error类型错误对象来表示函数调用状态
type error interface{
    Error() string
}

        标志库errors.New和fmt.Errorf函数用于创建实现error接口的错误对象。通过判断错误对象实例来确定具体错误类型。

        panic用来返回error接口对象:

  • Go实现类似try catch的异常处理

如何区分使用panic和error两种方式?

        导致关键流程出现不可修复性错误的使用panic,其它使用error(返回error)。 

 

 

 

 

 

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【ESP32S3 Sense接入语音识别+MiniMax模型+TTS模块语音播报】

【ESP32S3 Sense接入语音识别MiniMax模型TTS模块语音播报】 1. 前言2. 功能模块概述2.1 语音接入2.2 大模型接入2.3 TTS模块接入 3. 先决条件3.1 环境配置3.2 所需零件3.3 硬件连接步骤 4. 核心代码4.1 源码分享4.2 代码解析 5. 上传验证5.1 对话测试5.2 报错 6. 总结 1. 前言 …

Zabbix6 - Web管理网络拓扑/端口流量监控配置手册

Zabbix6 - Web管理网络拓扑/端口流量监控配置手册 概述: 1)Zabbix能监视各种网络参数,保证服务器系统的安全运营;并提供灵活的通知机制以让系统管理员快速定位/解决存在的各种问题。 Zabbix由两部分构成,Zabbix Server与可选组件Zabbix Agent。通过C/S模式采集数据,通过B…

element-ui divider 组件源码分享

今日简单分享 divider 组件&#xff0c;主要有以下两个方面&#xff1a; 1、divider 组件页面结构 2、divider 组件属性 一、组件页面结构 二、组件属性 2.1 direction 属性&#xff0c;设置分割线方向&#xff0c;类型 string&#xff0c;horizontal / vertical&#xff0…

《QT实用小工具·九》设备按钮控件

1、概述 源码放在文章末尾 该项目实现了设备按钮控件&#xff0c;主要包含如下功能&#xff1a; 可设置按钮样式 圆形、警察、气泡、气泡2、消息、消息2。可设置按钮颜色 布防、撤防、报警、旁路、故障。可设置报警切换及对应报警切换的颜色。可设置显示的防区号。可设置是否…

【多线程】震惊~这是我见过最详细的ReentrantLock的讲解

一.与synchronized相比ReentrantLock具有以下四个特点: 可中断&#xff1a;synchronized只能等待同步代码块执行结束&#xff0c;不可以中断&#xff0c;强行终断会抛出异常, 而reentrantlock可以调用线程的interrupt方法来中断等待&#xff0c;继续执行下面的代码。 在获取锁…

学浪m3u8视频解密

学浪的m3u8中的key进行了加密&#xff0c;而且还是难度比较高的vmp&#xff0c;为了让非程序专业用户能够下载学浪的视频&#xff0c;这里将学浪key解密和学浪获取课程集成在一个软件 小浪助手:专门下载学浪视频而生 小浪助手我打包在一起 链接&#xff1a;https://pan.baid…

【信号处理】基于变分自编码器(VAE)的图片典型增强方法实现

关于 深度学习中&#xff0c;经常面临图片数据量较小的问题&#xff0c;此时&#xff0c;对数据进行增强&#xff0c;显得比较重要。传统的图片增强方法包括剪切&#xff0c;增加噪声&#xff0c;改变对比度等等方法&#xff0c;但是&#xff0c;对于后端任务的性能提升有限。…

Redis 主从复制,哨兵模式,集群

目录 主从复制 主从复制 作用 缺陷 主从复制流程 实现Redis主从复制 哨兵模式 主从复制切换的缺点 哨兵的核心功能 哨兵模式原理 哨兵模式的作用 哨兵结构组成 故障转移机制 主节点的选举 实现哨兵模式 集群(Cluster) redis群集有三种模式&#xff0c;主从复制…

解决PDF打开后显示名称与文档名称不一致的问题【不需要word模板!!!】

文章目录 简介原因解决办法参考资料 简介 最近&#xff0c;博主在使用Acobat打开一个PDF文件的时候发现&#xff1a;打开后的PDF文件标签跟该文件的存储名称不一致。这是一件令人并不十分愉快和顺心的事情&#xff0c;网上搜索得到的解决办法基本上都是出奇的相似&#xff0c;…

css心跳动画

图标引入 <img class"icon" src"heart.svg" alt"" srcset""> CSS代码 <style>.icon {animation:bpm 1s linear,pulse 0.75s 1s linear infinite;}keyframes pulse {from,75%,to {transform: scale(1);}25% {transform:…

趣学前端 | 类,我想好好继承它的知识点

背景 最近睡前习惯翻会书&#xff0c;重温了《JavaScript权威指南》。这本书&#xff0c;文字小&#xff0c;内容多。两年了&#xff0c;我才翻到第十章。因为书太厚&#xff0c;平时都充当电脑支架。 JavaScript 类 话说当年类、原型、继承&#xff0c;差点给我绕晕。 在J…

生成式AI的情感实验——AI能否产生思想和情感?

机器人能感受到爱吗&#xff1f;这是一个很好的问题&#xff0c;也是困扰了科学家们很多年的科学未解之谜。虽然我们尚未准备好向智能机器赋予情感&#xff0c;但智能机器却已经可以借助生成式人工智能&#xff08;AI&#xff09;来帮助我们表达自己的情感。 自然情感表达 AI正…

【子集回溯】Leetcode 78. 子集 90. 子集 II

【子集回溯】Leetcode 78. 子集 90. 子集 II 78. 子集90. 子集 II ---------------&#x1f388;&#x1f388;78. 子集 题目链接&#x1f388;&#x1f388;------------------- 78. 子集 class Solution {List<List<Integer>> result new ArrayList<>()…

Java 7、Java 8常用新特性

目录 Java 8 常用新特性1、Lambda 表达式2、方法引用2.1 静态方法引用2.2 特定对象的实例方法引用2.3 特定类型的任意对象的实例方法引用2.4 构造器引用 3、接口中的默认方法4、函数式接口4.1 自定义函数式接口4.2 内置函数式接口 5、Date/Time API6、Optional 容器类型7、Stre…

【随笔】Git 基础篇 -- 分支与合并 git rebase(十)

&#x1f48c; 所属专栏&#xff1a;【Git】 &#x1f600; 作  者&#xff1a;我是夜阑的狗&#x1f436; &#x1f680; 个人简介&#xff1a;一个正在努力学技术的CV工程师&#xff0c;专注基础和实战分享 &#xff0c;欢迎咨询&#xff01; &#x1f496; 欢迎大…

【Pt】马灯贴图绘制过程 05-铁丝与渲染出图

目录 效果 步骤 一、基本材质 二、浮尘 三、渲染 效果 步骤 一、基本材质 CtrlAlt鼠标右键选中指定的纹理集 在智能材质中将“Iron Forged Old”加入图层 将智能材质“Iron Forged Old”文件夹打开&#xff0c;将图层“Base”和“Edge”的基本颜色改暗一点 二、浮尘 新…

PHP+python高校教务处工作管理系统q535p

开发语言&#xff1a;php 后端框架&#xff1a;Thinkphp/Laravel 前端框架&#xff1a;vue.js 服务器&#xff1a;apache 数据库&#xff1a;mysql 运行环境:phpstudy/wamp/xammp等 系统根据现有的管理模块进行开发和扩展&#xff0c;采用面向对象的开发的思想和结构化的开发方…

21.兼容性测试

考试频率低&#xff1b; 一般考兼容性测试会结合web测试&#xff1b;&#xff08;兼容性矩阵&#xff09; 主要议题&#xff1a; 1.兼容性测试概述 2.硬件兼容性测试 最低配置不讲究工作负载&#xff0c;意思是软件能够运行的最低要求环境&#xff1b; 推荐配置&#xff0c…

【精品方案】智慧金融大数据分析平台总体架构方案

以下是部分PPT内容&#xff0c;请您参阅。如需下载完整PPTX文件&#xff0c;请前往星球获取&#xff1a; 1.实现数据共享 通过数据平台实现数据集中&#xff0c;确保金融集团各级部门均可在保证数据隐私和安全的前提下使用数据&#xff0c;充分发挥数据作为企业重要资产的业务价…

Nacos 服务发现 快速入门

Nacos 服务发现 快速入门 一、Nacos 服务发现 – 什么是服务发现 &#xff1f; 1、 Nacos 服务发现-什么是服务发现 在微服务架构中&#xff0c;整个系统会按职责能力划分为多个服务&#xff0c;通过服务之间协作来实现业务目标。 这样在我们的代码中免不了要进行服务间的远程…