【Go-4】函数

函数

函数是编程中的基本构建块,用于封装可重用的代码逻辑。Go语言中的函数功能强大,支持多种特性,如多返回值、可变参数、匿名函数、闭包以及将函数作为值和类型传递。理解和掌握函数的使用对于编写高效、可维护的Go程序至关重要。本章将详细介绍Go语言中的函数,包括函数的定义与调用、参数和返回值、可变参数函数、匿名函数与闭包,以及函数作为值和类型的应用。

4.1 函数定义与调用

函数的基本定义

在Go语言中,函数使用func关键字定义。函数可以包含参数和返回值,也可以没有。

基本语法:

func 函数名(参数列表) (返回值列表) {// 函数体
}
  • func: 函数定义的关键字。
  • 函数名: 函数的名称,遵循标识符命名规则。
  • 参数列表: 函数接受的参数,可以有多个,每个参数需要指定类型。
  • 返回值列表: 函数返回的值,可以有多个,需指定类型。
  • 函数体: 包含函数执行的代码。

示例:

package mainimport "fmt"// 定义一个简单的函数,不接受参数,也不返回值
func sayHello() {fmt.Println("Hello, Go!")
}func main() {sayHello() // 调用函数
}

输出:

Hello, Go!
带参数的函数

函数可以接受多个参数,每个参数需要指定类型。参数之间用逗号分隔。

示例:

package mainimport "fmt"// 定义一个函数,接受两个整数参数并打印它们的和
func add(a int, b int) {sum := a + bfmt.Println("Sum:", sum)
}func main() {add(5, 3) // 调用函数,传递参数5和3
}

输出:

Sum: 8

参数类型简写:

当连续的参数具有相同的类型时,可以简化参数类型的声明。

示例:

package mainimport "fmt"// 简化参数类型声明
func multiply(a, b int) {product := a * bfmt.Println("Product:", product)
}func main() {multiply(4, 6) // 调用函数,传递参数4和6
}

输出:

Product: 24
带返回值的函数

函数可以返回一个或多个值。返回值需要在函数定义中指定。

示例:

package mainimport "fmt"// 定义一个函数,接受两个整数参数并返回它们的和
func add(a int, b int) int {return a + b
}func main() {sum := add(10, 15) // 调用函数,并接收返回值fmt.Println("Sum:", sum)
}

输出:

Sum: 25
多返回值函数

Go语言支持函数返回多个值,这在错误处理和复杂数据返回时非常有用。

示例:

package mainimport ("fmt""math"
)// 定义一个函数,返回两个值:平方根和平方
func calculate(x float64) (float64, float64) {sqrt := math.Sqrt(x)square := x * xreturn sqrt, square
}func main() {number := 16.0sqrt, square := calculate(number) // 接收多个返回值fmt.Printf("Number: %.2f, Square Root: %.2f, Square: %.2f\n", number, sqrt, square)
}

输出:

Number: 16.00, Square Root: 4.00, Square: 256.00
命名返回值

函数的返回值可以命名,这样在函数体内可以直接使用这些名字,并且可以使用return语句直接返回。

示例:

package mainimport "fmt"// 定义一个函数,返回两个命名的返回值
func divide(a, b float64) (quotient float64, remainder float64) {quotient = a / bremainder = math.Mod(a, b)return // 自动返回命名的返回值
}func main() {q, r := divide(10.5, 3.2)fmt.Printf("Quotient: %.2f, Remainder: %.2f\n", q, r)
}

输出:

Quotient: 3.28, Remainder: 0.90

4.2 函数参数和返回值

函数参数和返回值是函数与外界交互的主要方式。Go语言在参数传递和返回值处理上有其独特的特性。

参数传递方式

Go语言中的参数传递是按值传递,这意味着函数接收到的是参数的副本,对副本的修改不会影响原始变量。

示例:

package mainimport "fmt"// 定义一个函数,尝试修改参数的值
func modifyValue(x int) {x = 100fmt.Println("Inside modifyValue:", x)
}func main() {a := 50modifyValue(a)fmt.Println("After modifyValue:", a) // a的值不会被修改
}

输出:

Inside modifyValue: 100
After modifyValue: 50
使用指针传递参数

为了在函数内部修改外部变量的值,可以使用指针传递参数。指针传递允许函数直接访问和修改变量的内存地址。

示例:

package mainimport "fmt"// 定义一个函数,使用指针修改参数的值
func modifyPointer(x *int) {*x = 100fmt.Println("Inside modifyPointer:", *x)
}func main() {a := 50fmt.Println("Before modifyPointer:", a)modifyPointer(&a) // 传递变量a的地址fmt.Println("After modifyPointer:", a) // a的值被修改
}

输出:

Before modifyPointer: 50
Inside modifyPointer: 100
After modifyPointer: 100
多参数函数

Go语言支持多个参数的函数,可以组合使用不同类型的参数。

示例:

package mainimport "fmt"// 定义一个函数,接受多个不同类型的参数
func printDetails(name string, age int, height float64) {fmt.Printf("Name: %s, Age: %d, Height: %.2f\n", name, age, height)
}func main() {printDetails("Alice", 30, 5.6)printDetails("Bob", 25, 5.9)
}

输出:

Name: Alice, Age: 30, Height: 5.60
Name: Bob, Age: 25, Height: 5.90
可选参数

Go语言不直接支持可选参数,但可以通过参数的组合和使用指针来模拟实现。

示例:

package mainimport "fmt"// 定义一个函数,使用指针模拟可选参数
func greet(name string, title *string) {if title != nil {fmt.Printf("Hello, %s %s!\n", *title, name)} else {fmt.Printf("Hello, %s!\n", name)}
}func main() {var title string = "Dr."greet("Alice", &title) // 使用标题greet("Bob", nil)      // 不使用标题
}

输出:

Hello, Dr. Alice!
Hello, Bob!

4.3 可变参数函数

可变参数函数允许函数接受任意数量的参数。这在处理不确定数量输入时非常有用。Go语言通过在参数类型前加...来定义可变参数。

定义可变参数函数

基本语法:

func 函数名(参数类型, ...参数类型) 返回值类型 {// 函数体
}

示例:

package mainimport "fmt"// 定义一个函数,接受可变数量的整数参数并求和
func sum(nums ...int) int {total := 0for _, num := range nums {total += num}return total
}func main() {fmt.Println(sum(1, 2, 3))       // 输出: 6fmt.Println(sum(10, 20, 30, 40)) // 输出: 100fmt.Println(sum())              // 输出: 0
}

输出:

6
100
0
使用可变参数的其他示例

示例1:打印多个字符串

package mainimport "fmt"// 定义一个函数,接受可变数量的字符串参数并打印
func printStrings(strs ...string) {for _, s := range strs {fmt.Println(s)}
}func main() {printStrings("Go", "is", "fun")printStrings("Hello", "World")printStrings()
}

输出:

Go
is
fun
Hello
World

示例2:计算多个浮点数的平均值

package mainimport "fmt"// 定义一个函数,接受可变数量的浮点数参数并计算平均值
func average(nums ...float64) float64 {if len(nums) == 0 {return 0}total := 0.0for _, num := range nums {total += num}return total / float64(len(nums))
}func main() {fmt.Printf("Average: %.2f\n", average(1.5, 2.5, 3.5))       // 输出: Average: 2.50fmt.Printf("Average: %.2f\n", average(10.0, 20.0))         // 输出: Average: 15.00fmt.Printf("Average: %.2f\n", average())                  // 输出: Average: 0.00
}

输出:

Average: 2.50
Average: 15.00
Average: 0.00
将切片传递给可变参数函数

如果已经有一个切片,可以使用...操作符将切片元素作为可变参数传递给函数。

示例:

package mainimport "fmt"// 定义一个函数,接受可变数量的整数参数并求和
func sum(nums ...int) int {total := 0for _, num := range nums {total += num}return total
}func main() {numbers := []int{4, 5, 6}total := sum(numbers...) // 使用...将切片传递为可变参数fmt.Println("Total:", total) // 输出: Total: 15
}

输出:

Total: 15

4.4 匿名函数与闭包

匿名函数

匿名函数是没有名称的函数,可以在定义时直接调用,或赋值给变量以便后续使用。匿名函数在需要临时使用函数逻辑时非常有用。

示例1:立即调用匿名函数

package mainimport "fmt"func main() {// 定义并立即调用匿名函数func() {fmt.Println("This is an anonymous function!")}()// 带参数的匿名函数func(a, b int) {fmt.Printf("Sum: %d\n", a+b)}(3, 4)
}

输出:

This is an anonymous function!
Sum: 7

示例2:将匿名函数赋值给变量

package mainimport "fmt"func main() {// 将匿名函数赋值给变量greet := func(name string) {fmt.Printf("Hello, %s!\n", name)}greet("Alice")greet("Bob")
}

输出:

Hello, Alice!
Hello, Bob!
闭包

闭包是指一个函数可以访问其外部作用域中的变量,即使外部函数已经返回。闭包允许函数“记住”并操作其定义时的环境变量。

示例1:简单闭包

package mainimport "fmt"// 定义一个生成器函数,返回一个闭包
func generator() func() int {count := 0return func() int {count++return count}
}func main() {next := generator()fmt.Println(next()) // 输出: 1fmt.Println(next()) // 输出: 2fmt.Println(next()) // 输出: 3another := generator()fmt.Println(another()) // 输出: 1
}

输出:

1
2
3
1

解释:

  • generator函数返回一个匿名函数,该匿名函数访问并修改外部变量count
  • 每次调用next()时,count都会递增。
  • another是另一个闭包实例,拥有独立的count变量。

示例2:闭包与参数

package mainimport "fmt"// 定义一个函数,返回一个闭包,该闭包会将输入乘以指定的因子
func multiplier(factor int) func(int) int {return func(x int) int {return x * factor}
}func main() {double := multiplier(2)triple := multiplier(3)fmt.Println("Double 5:", double(5)) // 输出: Double 5: 10fmt.Println("Triple 5:", triple(5)) // 输出: Triple 5: 15
}

输出:

Double 5: 10
Triple 5: 15

解释:

  • multiplier函数接受一个factor参数,并返回一个闭包。
  • 该闭包接受一个整数x,并返回x乘以factor的结果。
  • doubletriple是两个不同的闭包实例,分别将输入数值乘以2和3。
闭包的应用场景
  • 延迟执行:将某些操作延迟到特定条件下执行。
  • 数据封装:封装数据,保护数据不被外部直接修改。
  • 回调函数:作为回调函数传递给其他函数,以实现灵活的功能扩展。

示例:延迟执行

package mainimport "fmt"// 定义一个函数,接受一个函数作为参数
func performOperation(operation func()) {fmt.Println("准备执行操作...")operation()fmt.Println("操作执行完毕。")
}func main() {performOperation(func() {fmt.Println("这是一个延迟执行的匿名函数。")})
}

输出:

准备执行操作...
这是一个延迟执行的匿名函数。
操作执行完毕。

4.5 函数作为值和类型

在Go语言中,函数可以作为值来传递和使用。这意味着函数可以被赋值给变量、作为参数传递给其他函数,甚至作为返回值返回。这种特性使得Go语言在函数式编程方面具有很大的灵活性。

将函数赋值给变量

函数可以被赋值给变量,从而实现对函数的引用和调用。

示例:

package mainimport "fmt"// 定义一个简单的函数
func sayHello() {fmt.Println("Hello!")
}func main() {// 将函数赋值给变量greeting := sayHello// 调用通过变量引用的函数greeting() // 输出: Hello!
}

输出:

Hello!
将函数作为参数传递

函数可以作为参数传递给其他函数,允许更高层次的抽象和代码复用。

示例:

package mainimport "fmt"// 定义一个函数类型
type operation func(int, int) int// 定义一个函数,接受另一个函数作为参数
func compute(a int, b int, op operation) int {return op(a, b)
}// 定义具体的操作函数
func add(a int, b int) int {return a + b
}func multiply(a int, b int) int {return a * b
}func main() {sum := compute(5, 3, add)product := compute(5, 3, multiply)fmt.Println("Sum:", sum)         // 输出: Sum: 8fmt.Println("Product:", product) // 输出: Product: 15
}

输出:

Sum: 8
Product: 15

解释:

  • operation是一个函数类型,接受两个整数并返回一个整数。
  • compute函数接受两个整数和一个operation类型的函数作为参数,并返回操作结果。
  • addmultiply是具体的操作函数,分别实现加法和乘法。
将函数作为返回值

函数可以作为其他函数的返回值,允许动态生成函数或实现高阶函数的功能。

示例:

package mainimport "fmt"// 定义一个函数,返回一个函数,该返回函数会将输入数值加上指定的值
func adder(x int) func(int) int {return func(y int) int {return x + y}
}func main() {addFive := adder(5)addTen := adder(10)fmt.Println("5 + 3 =", addFive(3))  // 输出: 5 + 3 = 8fmt.Println("10 + 7 =", addTen(7))  // 输出: 10 + 7 = 17
}

输出:

5 + 3 = 8
10 + 7 = 17

解释:

  • adder函数接受一个整数x,并返回一个匿名函数,该匿名函数接受另一个整数y,返回x + y的结果。
  • addFiveaddTen分别是不同的闭包实例,绑定了不同的x值。
使用函数作为数据结构的元素

函数可以被存储在数据结构中,如切片、Map等,提供更高的灵活性和扩展性。

示例1:将函数存储在切片中

package mainimport "fmt"// 定义一个函数类型
type operation func(int, int) intfunc main() {// 创建一个存储函数的切片operations := []operation{func(a, b int) int { return a + b },func(a, b int) int { return a - b },func(a, b int) int { return a * b },func(a, b int) int { return a / b },}a, b := 20, 5for _, op := range operations {result := op(a, b)fmt.Println(result)}
}

输出:

25
15
100
4

示例2:将函数存储在Map中

package mainimport "fmt"// 定义一个函数类型
type operation func(int, int) intfunc main() {// 创建一个存储函数的Mapoperations := map[string]operation{"add":      func(a, b int) int { return a + b },"subtract": func(a, b int) int { return a - b },"multiply": func(a, b int) int { return a * b },"divide":   func(a, b int) int { return a / b },}a, b := 15, 3for name, op := range operations {result := op(a, b)fmt.Printf("%s: %d\n", name, result)}
}

输出:

add: 18
subtract: 12
multiply: 45
divide: 5

解释:

  • 在第一个示例中,函数被存储在切片中,可以通过索引访问和调用。
  • 在第二个示例中,函数被存储在Map中,通过键名访问和调用,提供更具语义化的调用方式。
函数作为接口的实现

Go语言中的接口类型可以包含函数类型,使得接口的实现更加灵活。

示例:

package mainimport "fmt"// 定义一个接口,包含一个函数方法
type Greeter interface {Greet(name string) string
}// 定义一个结构体,实现Greeter接口
type Person struct {greeting string
}// 实现Greet方法
func (p Person) Greet(name string) string {return fmt.Sprintf("%s, %s!", p.greeting, name)
}func main() {var greeter Greetergreeter = Person{greeting: "Hello"}message := greeter.Greet("Alice")fmt.Println(message) // 输出: Hello, Alice!
}

输出:

Hello, Alice!

解释:

  • Greeter接口定义了一个Greet方法。
  • Person结构体实现了Greet方法,从而满足Greeter接口。
  • 通过接口类型变量greeter可以调用具体实现的Greet方法。

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

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

相关文章

【已解决】HBuilder X编辑器在外接显示器或者4K显示器怎么界面变的好小问题

触发方式:主要涉及DPI缩放问题,可能在电脑息屏有概率触发 修复方式: 1.先关掉软件直接更改屏幕缩放,然后打开软件,再关掉软件恢复原来的缩放,再打开软件就好了 2.(不推荐)右键HBuilder在属性里…

spark调度系统核心组件SparkContext、DAGSchedul、TaskScheduler、Taskset介绍

目录 1. SparkContext2.DAGScheduler3. TaskScheduler4. 协作关系5 TaskSet的定义6. 组件关系说明Spark调度系统的核心组件主要有SparkContext、DAGScheduler和TaskScheduler SparkContext介绍 1. SparkContext 1、资源申请: SparkContext是Spark应用程序与集群管理器(如St…

VSCode+EIDE通过KeilC51编译,使VSCode+EIDE“支持”C和ASM混编

在使用Keil C51时,要让Keil C51支持混编则需要在混编的.c文件上右键选择Options for File *(ALTF7),打开选项界面后,在 Properties 页 勾上 Generate Assembler SRC File 和 Assemble SRC File ,如下图所示: 这样设置后…

SQLynx:一款跨平台的企业级数据库管理工具

SQLynx 是一款支持跨平台(Windows、Linux、macOS、Web)的企业级数据库管理和 SQL 工具,可以提供高效、安全且适配国产化技术栈的数据库管理解决方案。 数据源 SQLynx 支持连接各种关系型数据库、非关系型数据库以及大数据平台,包…

实战项目8(实训)

目录 项目01 【sw1】配置 【sw2】配置 任务结果截图 项目02 【sw1】配置 【sw2】配置 任务结果截图 项目03 【sw1】配置 任务结果截图 项目04 【sw1】配置 【r1】配置 任务结果截图 项目05 【r1】配置 【r2】配置 【r3】配置 任务结果截图 项目06 【r1】…

TCP为什么是三次握手,而不是二次?

为什么需要三次握手? 想象一下,你要给远方的朋友寄一份重要文件。你会怎么做? 普通人的做法: 直接扔进邮箱,祈祷别丢了 聪明人的做法: 先打电话确认地址,再发快递,最后确认收到 T…

dubbo使用nacos作为注册中心配置

<dubbo:registry protocol"nacos" address"${dubbo.registry.address.nacos}" /> <dubbo:metadata-report address"${dubbo.metadata-report.address}"/> 如果有多个地址&#xff0c;这块如何配置呢&#xff1f; nacos://ip:端口?…

教师角色的转变:从知识传授者到学习引导者

教师角色的转变&#xff1a;从知识传授者到学习引导者 随着人工智能&#xff08;AI&#xff09;和信息技术的迅速发展&#xff0c;教育正在经历深刻的变革。其中&#xff0c;教师角色的转变尤为关键。传统上&#xff0c;教师主要承担“知识传授者”的职责&#xff0c;即向学生…

PostgreSQL 用户权限与安全管理

1 系统默认角色 postgres# select rolname from pg_roles; rolname ----------------------------- postgres pg_database_owner pg_read_all_data pg_write_all_data pg_monitor pg_read_all_settings pg_read_all_stats pg_stat_scan_tables …

C++构造函数和析构函数

C++构造函数和析构函数 C++的构造函数和析构函数是类的特殊成员函数,用于对象的创建和销毁,分别在对象的生命周期开始和结束时自动调用。它们的使用对资源管理和对象的初始化/清理至关重要。 1. 构造函数 定义 构造函数在对象创建时自动调用,用于初始化对象的数据成员。构造…

根据Cortex-M3(STM32F1)权威指南讲解MCU内存架构与如何查看编译器生成的地址具体位置

首先我们先查看官方对于Cortex-M3预定义的存储器映射 1.存储器映射 1.1 Cortex-M3架构的存储器结构 内部私有外设总线&#xff1a;即AHB总线&#xff0c;包括NVIC中断&#xff0c;ITM硬件调试&#xff0c;FPB, DWT。 外部私有外设总线&#xff1a;即APB总线&#xff0c;用于…

软件设计师“测试用例”考点分析——求三连

一、测试用例设计核心要点解析 1. 白盒测试覆盖标准 &#xff08;1&#xff09;路径覆盖&#xff1a;需覆盖程序中所有可能的路径。如2018年真题路径覆盖需要3组测试用例&#xff08;①②、①③、①③④&#xff09;&#xff0c;2020年流程图则需4个用例覆盖ace/abd/abe/acd四…

Linux 用户无法远程连接服务器

前言 昨天深夜一点多接到客户电话&#xff0c;客户说OS用户下午下班前还能正常登录。因为晚上一点半需要关闭所有服务进行迁移&#xff0c;但是用户无法登录了&#xff0c;导致后续流程无法执行。我让他先通过root用户紧急修改了密码&#xff0c;先保证业务正常流转。 问题 …

多模态大语言模型arxiv论文略读(八十八)

MammothModa: Multi-Modal Large Language Model ➡️ 论文标题&#xff1a;MammothModa: Multi-Modal Large Language Model ➡️ 论文作者&#xff1a;Qi She, Junwen Pan, Xin Wan, Rui Zhang, Dawei Lu, Kai Huang ➡️ 研究机构: ByteDance, Beijing, China ➡️ 问题背景…

svn迁移到git保留记录和Python字符串格式化 f-string的进化历程

svn迁移到git保留记录 and Python字符串格式化(二&#xff09;&#xff1a; f-string的进化历程 在将项目从SVN迁移到Git时&#xff0c;保留完整的版本历史记录非常重要。下面是详细的步骤和工具&#xff0c;可以帮助你完成这一过程&#xff1a; 安装Git和SVN工具 首先&#…

springboot配置mysql druid连接池,以及连接池参数解释

文章目录 前置配置方式参数解释 前置 springboot 项目javamysqldruid 连接池 配置方式 在 springboot 的 application.yml 中配置基本方式 # Druid 配置&#xff08;Spring Boot YAML 格式&#xff09; spring:datasource:url: jdbc:mysql://localhost:3306/testdb?useSSL…

vue实现高亮文字效果——advanced-mark.js

组件介绍-advanced-mark.js&#xff1a; advanced-mark.js 是一个用于 Vue 的高亮文字组件&#xff0c;它可以帮助你在文本中高亮显示指定的关键词或短语。 组件地址&#xff1a;https://angezid.github.io/advanced-mark.js/doc-v2/getting-started.html 主要功能&#xff1…

DC30V/2.5A同步降压芯片SL1581 输入24V降压5V 12V2A电流

在工业自动化、汽车电子等领域&#xff0c;24V 电源系统向 5V/12V 双轨供电的需求日益增长。针对这一痛点&#xff0c;森利威尔电子重磅推出 DC30V/2.5A 同步降压芯片 SL1581&#xff0c;凭借卓越的性能和创新设计&#xff0c;为工程师提供高可靠性、高性价比的电源解决方案。 …

React 第四十四节Router中 usefetcher的使用详解及注意事项

前言 useFetcher 是 React Router 中一个强大的钩子&#xff0c;用于在不触发页面导航的情况下执行数据加载&#xff08;GET&#xff09;或提交&#xff08;POST&#xff09;。 一、useFetcher 应用场景&#xff1a; 1、后台数据预加载&#xff08;如鼠标悬停时加载数据&…

Jmeter(三) - 测试计划(Test Plan)的元件

1.简介 上一篇已经教你如何通过JMeter来创建一个测试计划&#xff08;Test Plan&#xff09;&#xff0c;那么这一篇我们就将JMeter启动起来&#xff0c;创建一个测试计划&#xff08;Test plan&#xff09;&#xff0c;给大家介绍一下测试计划&#xff08;Test Plan&#xff…