【Go语言基础【13】】函数、闭包、方法

文章目录

  • 零、概述
  • 一、函数基础
    • 1、函数基础概念
    • 2、参数传递机制
    • 3、返回值特性
      • 3.1. 多返回值
      • 3.2. 命名返回值
      • 3.3. 错误处理
  • 二、函数类型与高阶函数
    • 1. 函数类型定义
    • 2. 高阶函数(函数作为参数、返回值)
  • 三、匿名函数与闭包
    • 1. 匿名函数(Lambda函数)
    • 2. 闭包(Closure)
  • 四、内置函数
  • 五、方法(让go有了面向对象的特性)

零、概述

函数核心特性速查

特性说明
多返回值支持同时返回结果和错误(如(int, error)
可变参数通过...Type定义,本质为切片([]Type
函数类型可定义函数类型(如type FuncType func(int) bool
闭包匿名函数捕获外部变量,保持状态
方法绑定到结构体的函数,通过接收者参数(func (t Type) Method())定义
内置函数lenmakeappend等预定义函数,直接使用

最佳实践

  1. 错误处理
    • 多返回值中优先返回错误(如func() (Result, error))。
    • 使用if err != nil判断错误,避免忽略重要异常。
  2. 减少值拷贝
    • 传递大结构体时使用指针(*T),避免性能损耗。
    • 切片、map等引用类型默认传递指针,无需额外处理。
  3. 函数职责单一
    • 每个函数专注完成一个独立功能,符合单一职责原则。
    • 避免函数过长(建议不超过50行),复杂逻辑拆分为子函数。

 

一、函数基础

1、函数基础概念

Go语言中的函数可赋值给变量、作为参数传递或作为返回值,极大提升了编程灵活性。

1. 函数定义语法

func 函数名(参数列表) (返回值列表) {函数体return 返回值
}
  • 参数列表:参数类型需后置,连续相同类型可合并声明。
    func sum(x, y int) int { // x, y 均为 int 类型return x + y
    }
    
  • 返回值列表:可无返回值或多返回值(用括号包裹),支持命名返回值。
    func div(x, y int) (int, error) { // 多返回值if y == 0 {return 0, errors.New("除数不能为0")}return x / y, nil
    }
    

 

2、参数传递机制

1.1. 值传递

  • 机制:传递参数的副本,函数内修改不影响原始值。
  • 适用场景:小数据类型(如intstring)或无需修改原始值的场景。
    func modifyValue(x int) {x = 100 // 仅修改副本
    }
    func main() {a := 20modifyValue(a) // a 仍为 20
    }
    

1.2. 引用传递

  • 机制:传递参数的地址(通过指针*T),函数内可修改原始值。
  • 适用场景:大数据类型(如结构体)或需修改原始值的场景。
    func modifyPointer(x *int) {*x = 100 // 修改原始值
    }
    func main() {a := 20modifyPointer(&a) // a 变为 100
    }
    

1.3. 可变参数
语法:通过...Type定义可变参数,本质为切片。

func sum(nums ...int) int { // nums 类型为 []inttotal := 0for _, num := range nums {total += num}return total
}
func main() {sum(1, 2, 3) // 等价于 sum([]int{1,2,3})
}

 

3、返回值特性

3.1. 多返回值

用途:同时返回结果和错误(Go语言的惯用模式)。

func readFile(path string) ([]byte, error) {data, err := os.ReadFile(path)return data, err
}

3.2. 命名返回值

语法:为返回值命名,函数体可直接使用(类似变量声明)。

func calculate(x, y int) (sum, product int) { // 命名返回值sum = x + yproduct = x * yreturn // 隐式返回 sum, product
}

3.3. 错误处理

丢弃返回值:用_忽略不关心的返回值。

data, _ := readFile("data.txt") // 忽略错误

 

二、函数类型与高阶函数

1. 函数类型定义

在Go语言中,函数类型定义是指为一种特定的函数签名创建一个自定义类型名称。这样可以让具有相同签名的函数共享同一个类型(有点面向接口开发的感觉)。

type Calculator func(int, int) int // 定义函数类型// 这些函数都符合Calculator类型的签名
func add(x, y int) int { return x + y }
func subtract(x, y int) int { return x - y }
func multiply(x, y int) int { return x * y }
func divide(x, y int) int { return x / y }

 

例题:

package mainimport "fmt"type Calculator func(int, int) intfunc add(x, y int) int      { return x + y }
func subtract(x, y int) int { return x - y }// 使用函数类型作为参数
func calculate(calc Calculator, a, b int) int {return calc(a, b)
}// 使用函数类型作为变量
func main() {var myCalc CalculatormyCalc = addfmt.Println(myCalc(5, 3)) // 输出: 8myCalc = subtractfmt.Println(myCalc(5, 3)) // 输出: 2// 作为参数传递result := calculate(add, 10, 5)fmt.Println(result) // 输出: 15
}

 

2. 高阶函数(函数作为参数、返回值)

函数作为参数

func operate(x, y int, fn Calculator) int { // 接收函数作为参数
// 作为参数的函数,来处理参数return fn(x, y)
}
func main() {result := operate(10, 5, add) // 调用 add 函数
}

 
函数作为返回值
在Go语言中,函数可以作为返回值,这使得我们可以创建闭包。闭包是一个函数,它可以捕获并记住其所在环境中的变量。

func makeAdder(n int) Calculator { // 返回函数return func(x int) int {return x + n}
}
func main() {add5 := makeAdder(5) // add5(3) 返回 8
}

 

三、匿名函数与闭包

1. 匿名函数(Lambda函数)

  • 无名称函数,可直接定义和调用
 // 赋值给变量greet := func() {fmt.Println("Hello, Go!")}greet() // 调用匿名函数1. 定义:func() { ... }是一个匿名函数,因为它没有名称。
2. 赋值:我们将这个匿名函数赋值给变量greet。
3. 调用:通过greet()来调用这个匿名函数。// 立即执行函数表达式(IIFE)result := func(x, y int) int {return x * y}(3, 4) 1. 定义并执行:func(x, y int) int { return x * y }是一个匿名函数。
2. 立即执行:在定义后面紧跟(3, 4),这表示立即用参数34调用这个函数。
3. 结果:函数返回3 * 4的结果12,并将其赋值给变量result。

 

2. 闭包(Closure)

定义:匿名函数捕获外部变量形成闭包,变量在闭包内保持状态。

闭包能够捕获并记住其外部环境中的变量,即使在函数执行完毕后,这些变量仍然可以被访问和修改。这使得闭包可以在不同的调用之间保持状态,例如计数器、缓存等。

package mainimport "fmt"// adder 返回一个闭包函数,该函数捕获了外部变量 sum
func adder() func(int) int {sum := 0 // 这是被闭包捕获的外部变量return func(x int) int {sum += x // 每次调用时,更新并返回 sumreturn sum}
}func main() {pos := adder() // 创建一个新的闭包fmt.Println(pos(1)) // 输出: 1fmt.Println(pos(2)) // 输出: 3fmt.Println(pos(3)) // 输出: 6// 创建另一个独立的闭包neg := adder()fmt.Println(neg(-1)) // 输出: -1fmt.Println(neg(-2)) // 输出: -3
}

 

四、内置函数

Go语言预定义了一组内置函数,无需导入包即可使用:

函数名功能描述
len()获取切片、字符串、通道等的长度
new()分配零值内存,返回指针(如new(int)返回*int
make()创建引用类型(切片、map、通道)并初始化
append()向切片追加元素,返回新切片
panic()触发运行时恐慌,用于错误处理
recover()defer中恢复恐慌,避免程序崩溃

 

五、方法(让go有了面向对象的特性)

在Go语言中,方法和函数的主要区别在于方法是绑定到特定类型的,而函数则是独立的。

package mainimport ("fmt""math"
)type Circle struct {radius float64
}func (c Circle) area() float64 {return math.Pi * c.radius * c.radius
}func (c *Circle) updateRadius(r float64) {c.radius = r
}func calculateArea(radius float64) float64 {return math.Pi * radius * radius
}func main() {c := Circle{radius: 5}// 调用方法fmt.Println("Area using method:", c.area()) // 使用方法计算面积// 更新半径c.updateRadius(10)fmt.Println("Updated area using method:", c.area())// 调用函数fmt.Println("Area using function:", calculateArea(5)) // 使用函数计算面积
}

通过方法,Go语言实现了面向对象编程的特性,使得类型可以拥有自己的行为。

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

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

相关文章

网络编程之服务器模型与UDP编程

一、服务器模型 在网络通信中,通常要求一个服务器连接多个客户端 为了处理多个客户端的请求,通常有多种表现形式 1、循环服务器模型 一个服务器可以连接多个客户端,但同一时间只能连接并处理一个客户的请求 socket() 结构体 bind() listen() …

open3D:三维点云处理

open3d 点云数据处理 爆肝5万字❤️Open3D 点云数据处理基础(Python版)_python 点云 焊缝-CSDN博客 如何用NumPy读取和保存点云数据 - 知乎 读取并可视化点云 np.loadtxt 从txt中读取点集,并open3d显示单个点云 txt内容:每行皆…

使用联邦多轨迹图神经网络(GNNs)结合稀缺数据预测婴儿脑连接|文献速递-深度学习医疗AI最新文献

Title 题目 Predicting infant brain connectivity with federated multi-trajectory GNNs using scarce data 使用联邦多轨迹图神经网络(GNNs)结合稀缺数据预测婴儿脑连接 01 文献速递介绍 多模态影像下的婴儿脑连接演化预测:联邦学习与…

[特殊字符] 深入理解 Linux 内核进程管理:架构、核心函数与调度机制

Linux 内核作为一个多任务操作系统,其进程管理子系统是核心组成部分之一。无论是用户应用的运行、驱动行为的触发,还是系统调度决策,几乎所有操作都离不开进程的创建、调度与销毁。本文将从进程的概念出发,深入探讨 Linux 内核中进…

第16节 Node.js 文件系统

Node.js 提供一组类似 UNIX(POSIX)标准的文件操作API。 Node 导入文件系统模块(fs)语法如下所示: var fs require("fs") 异步和同步 Node.js 文件系统(fs 模块)模块中的方法均有异步和同步版本&#xff…

《探秘局域网广播:网络世界的 “大喇叭”》

揭开局域网广播的神秘面纱 在当今数字化时代,网络已成为人们生活和工作中不可或缺的一部分。从日常的网页浏览、社交媒体互动,到企业级的数据传输、云计算应用,网络通信无处不在。在这个庞大而复杂的网络世界里,数据如同信息流在各个节点之间穿梭,而局域网广播则是其中一种…

基于Ubuntu22.04安装SVN服务器之仓库迁移

基于Ubuntu22.04安装SVN服务器之仓库迁移 第一步: 停止svn服务器 第一步: 停止svn服务器 1)建议迁移的时候先把SN服务器停掉,以免操作失败。 svnserve -d -r /usr/svn第二步:dump出svn代码库 1)通过dump出旧的svn服务器上的代码…

Unity UI 性能优化终极指南 — Image篇

🎯 Unity UI 性能优化终极指南 — Image篇 🧩 Image 是什么? Image 是UGUI中最常用的基本绘制组件支持显示 Sprite,可以用于背景、按钮图标、装饰等是UI性能瓶颈的头号来源之一,直接影响Draw Call和Overdraw &#x1…

「Java基本语法」代码格式与注释规范

Java代码的基本格式 Java代码的规范格式是编写和维护Java程序的基础,其中包括类定义、方法定义、代码缩进、大括号位置等。 1.核心规则 每个Java文件必须包含一个公共类(public class),且Java源文件的文件名必须和这…

2025年AI编程工具推荐

目录 👑 **一、全能型AI开发环境(IDE)**🛠️ **二、AI代码助手与插件**🎯 **三、垂直领域工具**🇨🇳 **四、国产工具精选**🔮 **五、创新前沿工具**⚖️ **选型建议** 2025年&#x…

【工具使用】STM32CubeMX-FreeRTOS操作系统-信号标志、互斥锁、信号量篇

一、概述 无论是新手还是大佬,基于STM32单片机的开发,使用STM32CubeMX都是可以极大提升开发效率的,并且其界面化的开发,也大大降低了新手对STM32单片机的开发门槛。     本文主要讲述STM32芯片FreeRTOS信号标志、互斥锁和信号…

ArrayList和LinkedList(深入源码加扩展)

ArrayList 和 LinkedList 是 Java 集合框架中两种常用的列表实现,它们在底层数据结构、性能特点和适用场景上有显著的区别。以下是它们的详细对比以及 ArrayList 的扩容机制。 1. ArrayList 和 LinkedList 的底层区别 (1) 底层数据结构 ArrayList: 基于动态数组(Dynamic Ar…

浅谈 React Suspense

React Suspense 是 React 中用于处理异步操作的功能。它可以让你"等待"某些操作,如数据获取或组件加载完成,然后再渲染组件。Suspense 的核心理念是让组件在准备好之前显示一个备用的 UI,例如加载指示器,从而提高用户体…

机器学习的数学基础:线性模型

线性模型 线性模型的基本形式为: f ( x ) ω T x b f\left(\boldsymbol{x}\right)\boldsymbol{\omega}^\text{T}\boldsymbol{x}b f(x)ωTxb 回归问题 利用最小二乘法,得到 ω \boldsymbol{\omega} ω和 b b b的参数估计$ \boldsymbol{\hat{\omega}}…

Linux【4】------RK3568启动和引导顺序

引导顺序 RK3568 的启动流程如下: 加电后,芯片首先执行 BootROM 中的代码; BootROM 会尝试从配置好的外部设备(如 NOR/NAND/eMMC/SD 卡)加载启动程序; 如果这些设备都没有有效的启动代码,Bo…

Deepseek/cherry studio中的Latex公式复制到word中

需要将Deepseek/cherry studio中公式复制到word中,但是deepseek输出Latex公式,比如以下Latex代码段,需要通过Mathtype翻译才能在word中编辑。 $$\begin{aligned}H_1(k1) & H_1(k) \frac{1}{A_1} \left( Q_1 u_1(k) Q_{i1} - Q_2 u_2(k…

关于iview组件中使用 table , 绑定序号分页后序号从1开始的解决方案

问题描述:iview使用table 中type: "index",分页之后 ,索引还是从1开始,试过绑定后台返回数据的id, 这种方法可行,就是后台返回数据的每个页面id都不完全是按照从1开始的升序,因此百度了下,找到了…

【机器学习】支持向量机实验报告——基于SVM进行分类预测

目录 一、实验题目描述 二、实验步骤 三、Python代码实现基于SVM进行分类预测 四、我的收获 五、我的感受 一、实验题目描述 实验题目:基于SVM进行分类预测 实验要求:通过给定数据,使用支持向量机算法(SVM)实现分…

前端开发面试题总结-JavaScript篇(二)

文章目录 其他高频问题15、JS的数据类型有哪些16、如何判断数组类型?17、解释 this 的指向规则18、跨域问题及解决方案19、宏任务与微任务的区别是什么?列举常见的宏任务和微任务。20、为什么微任务的优先级高于宏任务?设计目的是什么&#x…

硬件电路设计-开关电源设计

硬件电路设计-开关电源 电容选取设置输出电压电感的选取PCB布局典型电路 这里以杰华特的JW5359M 开关电源为例,介绍各个部分的功能电路。 当EN引脚电压低于0.4V时,整个稳压器关闭,稳压器消耗的电源电流降至1μΑ以下 电容选取 1.C1和C25构成…