Go 语言函数

简介: 函数是基本的代码块,用于执行一个任务Go 语言最少有一个 main() 函数你可以通过函数来划分不同功能,逻辑上每个函数执行的是指定的任务函数声明告诉了编译器函数的名称,返回类型,和参数Go 语言标准库提供了多种可动用的内置的函数。

1. 概述



函数是基本的代码块,用于执行一个任务

Go 语言最少有一个 main() 函数

你可以通过函数来划分不同功能,逻辑上每个函数执行的是指定的任务

函数声明告诉了编译器函数的名称,返回类型,和参数


Go 语言标准库提供了多种可动用的内置的函数。


例如,len()函数可以接受不同类型参数并返回该类型的长度。如果我们传入的是字符串则返回字符串的长度,如果传入的是数组,则返回数组中包含的元素个数。


2. 函数的定义


Go 语言函数定义格式如下:

func 函数名 (参数列表) (返回值列表)


无参数返回值

func test() {
}

传参有返回值

func test(a int,b int) int {
 return n
}


传参有多个返回值

func result(a int,b int)(int,int) {
 return a+b,a*b
}



  • 示例: 定义max()函数传入两个整形参数 num1 和 num2,并返回这两个参数的最大值
/* 函数返回两个数的最大值 */
func max(num1, num2 int) int {
 /* 声明局部变量 */
 var result int
 if num1 > num2 {
  result = num1
 } else {
  result = num2
 }
 return result
}


3. 函数调用


  • 当创建函数时,你定义了函数需要做什么,通过调用该函数来执行指定任务,调用函数,向函数传递参数,并返回值


函数的调用


package main
import "fmt"
func main() {
 var (
  a int = 100
  b int = 200
 )
 var result int
 //函数调用,注意返回值和接受值类型必须一致
 result = max(a, b)
 fmt.Println("最大值为:", result)
}
/* 函数返回两个数的最大值 */
func max(num1, num2 int) int {
 /* 声明局部变量 */
 var result int
 if num1 > num2 {
  result = num1
 } else {
  result = num2
 }
 return result
}
//输出结果如下
最大值为: 200

函数返回多个值


package main
import "fmt"
func swap(x, y string) (string, string) {
 return y, x
}
func main() {
 a, b := swap("hello", "world")
 fmt.Println(a, b)
}
//输出结果如下
world hello
package main
import "fmt"
func main() {
 var (
  a, b = multi_value(3, 5)
 )
 fmt.Println("和:", a, "\n积:", b)
}
func multi_value(num1, num2 int) (int, int) {
 result1 := num1 + num2
 result2 := num1 * num2
 //返回两数和,两数乘积
 return result1, result2
}
//输出结果如下
和: 8 
积: 15

 

4. 函数参数


  • 函数如果使用参数,该变量可称为函数的形参
  • 形参就像定义在函数体内的局部变量
  • 调用函数,可以通过两种方式来传递参数

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

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

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



值类型


package main
import "fmt"
func main() {
 var (
  num1 = 10
  num2 = 20
 )
 fmt.Println("交换前\n")
 fmt.Printf("num1: %v\n", num1)
 fmt.Printf("num2: %v\n", num2)
 swap(num1, num2)
 fmt.Println("\n交换后\n")
 fmt.Printf("num1: %v\n", num1)
 fmt.Printf("num2: %v\n", num2)
}
//两数交换
func swap(a, b int) {
 a, b = b, a
}
//输出结果如下
交换前
num1: 10
num2: 20
交换后
num1: 10
num2: 20


总结:


可以看到,实际的值并没有交换,只是把实际参数给复制了一份传递到了函数中去,并不会影响原本的实际参数


引用类型


package main
import "fmt"
func main() {
 var (
  num1 = 11
  num2 = 22
 )
 fmt.Println("交换前")
 fmt.Printf("num1: %v, num2: %v\n", num1, num2)
 fmt.Println("-----------------")
 swap(&num1, &num2)
 fmt.Println("交换后")
 fmt.Printf("num1: %v, num2: %v\n", num1, num2)
}
//引用类型交换
func swap(a, b *int) {
 *a, *b = *b, *a
}
//输出结果如下
交换前
num1: 11, num2: 22
-----------------
交换后
num1: 22, num2: 11


5. 函数作为实参


  • 可以很灵活的创建函数,并作为另一个函数的实参
  • 示例:

使用math数学包


package main
import (
 "fmt"
 "math"
)
func main() {
 //函数变量
 result := func(x float64) float64 {
  return math.Sqrt(x)
 }
 fmt.Println(result(9))
 fmt.Println("-10的绝对值:", math.Abs(-10))
 fmt.Println("5.2向下取整:", math.Ceil(5.2))
 fmt.Println("5.8向下取整:", math.Floor(5.8))
 fmt.Println("11除3的余数:", math.Mod(11, 3))
 fmt.Println("取整数,取小数")
 fmt.Println(math.Modf(5.26))
 fmt.Println("3的2次方", math.Pow(3, 2))
 fmt.Println("10的4次方", math.Pow10(4))
 fmt.Println("8的开平方", math.Sqrt(8))
 fmt.Println("8的开立方", math.Cbrt(8))
 fmt.Println("圆周率", math.Pi)
}
//输出结果如下
3
-10的绝对值: 10
5.2向下取整: 6
5.8向下取整: 5
11除3的余数: 2
取整数,取小数
5 0.2599999999999998
3的2次方 9
10的4次方 10000
8的开平方 2.8284271247461903
8的开立方 2
圆周率 3.141592653589793

 

6. 回调函数


回调函数是一个被作为参数传递的函数,可以大大提升编程的效率。

函数执行流程描述


① 首先在main函数中调用了test1()函数,这时候test1会顺着去加载里面的语句 1、2,直到语句2加载完后,加载回调函数test2,test2会执行函数体内的语句 1、2,3;

② 当test2函数体中语句执行完成,回调函数的生命周期将结束,如果有返回值将返回,没有则结束;

③ 当回调函数执行完以后,将继续执行test1函数中的语句 3,4,执行完以后test1函数的生命周期也将结束

④ test1函数结束以后就会返回main主函数


2387773-20220328125810354-2060810095.png


package main
import "fmt"
//声明函数类型
type cback func(int) int
func main() {
 //对回调函数进行隐匿,能起到保护作用,并且提高程序的运行效率
 test_cback(1, callback)
}
//测试函数,用来调用回调函数
func test_cback(x int, f cback) {
 fmt.Println("test_back函数:语句1")
 f(x)
 fmt.Println("test_back函数:语句2")
}
//回调函数
func callback(a int) int {
 fmt.Println("回调函数callback:", a)
 return a
}
//执行结果如下
test_back函数:语句1
回调函数callback: 1
test_back函数:语句2


7. 匿名函数


  • 匿名函数就是没有函数名的函数,匿名函数多用于实现回调函数和闭包
  • 匿名函数是一个"内联"语句或表达式
  • 匿名函数的优越性在于可以直接使用函数内的变量,不必申明
  • 匿名函数的定义格式如下:

func(参数)(返回值){

函数体

}



匿名函数的执行方式如下


  • ① 把匿名函数赋值给变量
package main
import "fmt"
func main() {
 sayHello := func() {
  fmt.Println("匿名函数")
 }
 sayHello()
}
//执行结果如下
匿名函数
• ② 立即执行函数
package main
import "fmt"
func main() {
 func() {
  fmt.Println("匿名函数")
 }() //匿名函数定义完成加上()可以直接进行执行
}
//执行结果如下
匿名函数

 

8. 闭包


  • 闭包可以理解为定义在一个函数内部的函数,本质上闭包是将函数内部和外部连接起来的桥梁,或者说是函数和其引用环境的组合体
  • 闭包指的是一个函数和与其相关的引用环境组合而成的实体。简单来说,闭包=函数+引用环境


闭包示例 ①


package main
import "fmt"
//定义一个函数,他的返回值是一个函数
//把函数作为返回值
func a() func() {
 name := "world"
 return func() {
  fmt.Println("hello", name) //先在函数内找name变量,找不到就上函数体外层去找
 }
}
func main() {
 //闭包 = 函数+外出变量的引用
 r := a() //r就是一个闭包
 r()      //相当于执行了a函数的匿名函数
}
//执行结果
hello world


2387773-20220328125214415-313362656.png


示例 ②


package main
import (
 "fmt"
 "strings"
)
//使用闭包做文件后缀名检测
func makeSuffixFunc(suffix string) func(string) string {
 return func(name string) string {
  if !strings.HasSuffix(name, suffix) {//判断字符串后缀的格式
   return name + suffix
  }
  return name
 }
}
func main() {
 jpgFunc := makeSuffixFunc(".jpg")
 txtFunc := makeSuffixFunc(".txt")
 fmt.Println(jpgFunc("test"))
 fmt.Println(txtFunc("test"))
}
//执行结果如下
test.jpg
test.txt

示例 ③


package main
import "fmt"
func calc(base int) (func(int) int, func(int) int) {
 add := func(i int) int {
  base += i
  return base
 }
 sub := func(i int) int {
  base -= i
  return base
 }
 return add, sub
}
func main() {
 x, y := calc(100)
 ret1 := x(200)//调用x的时候传入值为200;base = 100 + 200
 fmt.Printf("ret1: %v\n", ret1)
 ret2 := y(200)//base = 300 - 200
 fmt.Printf("ret2: %v\n", ret2)
}
//输出结果如下
ret1: 300
ret2: 100

 

  • 示例 ④


//回调函数是把函数作为传递参数,而闭包则是把函数作为一个返回值
package main
import "fmt"
func close_package() func() int {
 i := 0
 return func() int {
  i += 1
  return i
 }
}
func main() {
 //定义函数 使用闭包做+1操作
 nextNumber := close_package()
 fmt.Println("使用nextNumber做自增")
 fmt.Println(nextNumber())
 fmt.Println(nextNumber())
 fmt.Println(nextNumber())
 fmt.Println("使用nextNumber1做自增")
 nextNumber1 := close_package()
 fmt.Println(nextNumber1())
 fmt.Println(nextNumber1())
 fmt.Println(nextNumber1())
}
//执行结果如下
使用nextNumber做自增
1
2
3
使用nextNumber1做自增
1
2
3
相关文章
|
3天前
|
运维 监控 算法
监控局域网其他电脑:Go 语言迪杰斯特拉算法的高效应用
在信息化时代,监控局域网成为网络管理与安全防护的关键需求。本文探讨了迪杰斯特拉(Dijkstra)算法在监控局域网中的应用,通过计算最短路径优化数据传输和故障检测。文中提供了使用Go语言实现的代码例程,展示了如何高效地进行网络监控,确保局域网的稳定运行和数据安全。迪杰斯特拉算法能减少传输延迟和带宽消耗,及时发现并处理网络故障,适用于复杂网络环境下的管理和维护。
|
4天前
|
编译器 Go
揭秘 Go 语言中空结构体的强大用法
Go 语言中的空结构体 `struct{}` 不包含任何字段,不占用内存空间。它在实际编程中有多种典型用法:1) 结合 map 实现集合(set)类型;2) 与 channel 搭配用于信号通知;3) 申请超大容量的 Slice 和 Array 以节省内存;4) 作为接口实现时明确表示不关注值。此外,需要注意的是,空结构体作为字段时可能会因内存对齐原因占用额外空间。建议将空结构体放在外层结构体的第一个字段以优化内存使用。
|
8天前
|
存储 Go
Go 语言入门指南:切片
Golang中的切片(Slice)是基于数组的动态序列,支持变长操作。它由指针、长度和容量三部分组成,底层引用一个连续的数组片段。切片提供灵活的增减元素功能,语法形式为`[]T`,其中T为元素类型。相比固定长度的数组,切片更常用,允许动态调整大小,并且多个切片可以共享同一底层数组。通过内置的`make`函数可创建指定长度和容量的切片。需要注意的是,切片不能直接比较,只能与`nil`比较,且空切片的长度为0。
Go 语言入门指南:切片
|
12天前
|
算法 安全 Go
公司局域网管理系统里的 Go 语言 Bloom Filter 算法,太值得深挖了
本文探讨了如何利用 Go 语言中的 Bloom Filter 算法提升公司局域网管理系统的性能。Bloom Filter 是一种高效的空间节省型数据结构,适用于快速判断元素是否存在于集合中。文中通过具体代码示例展示了如何在 Go 中实现 Bloom Filter,并应用于局域网的 IP 访问控制,显著提高系统响应速度和安全性。随着网络规模扩大和技术进步,持续优化算法和结合其他安全技术将是企业维持网络竞争力的关键。
26 2
公司局域网管理系统里的 Go 语言 Bloom Filter 算法,太值得深挖了
|
4天前
|
存储 缓存 监控
企业监控软件中 Go 语言哈希表算法的应用研究与分析
在数字化时代,企业监控软件对企业的稳定运营至关重要。哈希表(散列表)作为高效的数据结构,广泛应用于企业监控中,如设备状态管理、数据分类和缓存机制。Go 语言中的 map 实现了哈希表,能快速处理海量监控数据,确保实时准确反映设备状态,提升系统性能,助力企业实现智能化管理。
22 3
|
8天前
|
开发框架 前端开发 Go
eino — 基于go语言的大模型应用开发框架(二)
本文介绍了如何使用Eino框架实现一个基本的LLM(大语言模型)应用。Eino中的`ChatModel`接口提供了与不同大模型服务(如OpenAI、Ollama等)交互的统一方式,支持生成完整响应、流式响应和绑定工具等功能。`Generate`方法用于生成完整的模型响应,`Stream`方法以流式方式返回结果,`BindTools`方法为模型绑定工具。此外,还介绍了通过`Option`模式配置模型参数及模板功能,支持基于前端和用户自定义的角色及Prompt。目前主要聚焦于`ChatModel`的`Generate`方法,后续将继续深入学习。
102 7
|
4天前
|
存储 缓存 安全
Go 语言中的 Sync.Map 详解:并发安全的 Map 实现
`sync.Map` 是 Go 语言中用于并发安全操作的 Map 实现,适用于读多写少的场景。它通过两个底层 Map(`read` 和 `dirty`)实现读写分离,提供高效的读性能。主要方法包括 `Store`、`Load`、`Delete` 等。在大量写入时性能可能下降,需谨慎选择使用场景。
|
8天前
|
存储 开发框架 Devops
eino — 基于go语言的大模型应用开发框架(一)
Eino 是一个受开源社区优秀LLM应用开发框架(如LangChain和LlamaIndex)启发的Go语言框架,强调简洁性、可扩展性和可靠性。它提供了易于复用的组件、强大的编排框架、简洁明了的API、最佳实践集合及实用的DevOps工具,支持快速构建和部署LLM应用。Eino不仅兼容多种模型库(如OpenAI、Ollama、Ark),还提供详细的官方文档和活跃的社区支持,便于开发者上手使用。
74 8
|
18天前
|
监控 Linux PHP
【02】客户端服务端C语言-go语言-web端PHP语言整合内容发布-优雅草网络设备监控系统-2月12日优雅草简化Centos stream8安装zabbix7教程-本搭建教程非docker搭建教程-优雅草solution
【02】客户端服务端C语言-go语言-web端PHP语言整合内容发布-优雅草网络设备监控系统-2月12日优雅草简化Centos stream8安装zabbix7教程-本搭建教程非docker搭建教程-优雅草solution
68 20
|
8天前
|
存储 算法 Go
Go语言实战:错误处理和panic_recover之自定义错误类型
本文深入探讨了Go语言中的错误处理和panic/recover机制,涵盖错误处理的基本概念、自定义错误类型的定义、panic和recover的工作原理及应用场景。通过具体代码示例介绍了如何定义自定义错误类型、检查和处理错误值,并使用panic和recover处理运行时错误。文章还讨论了错误处理在实际开发中的应用,如网络编程、文件操作和并发编程,并推荐了一些学习资源。最后展望了未来Go语言在错误处理方面的优化方向。

热门文章

最新文章