Go 专栏|函数那些事

简介: Go 专栏|函数那些事

QQ图片20220423132354.png

函数定义


函数包括以下几个部分:关键词 func,函数名,参数列表,返回列表和函数体。


func name(param-list) ret-list {
  body
}
复制代码


函数可以没有参数,也可以没有返回值。


func funcA() {
  fmt.Println("i am funcA") // i am funcA
}
复制代码


函数的类型称作函数签名,当两个函数的参数列表和返回列表相同时,则两个函数的类型或签名就相同。


func add(x int, y int) int {
  return x + y
}
func sub(x int, y int) (z int) {
  z = x - y
  return
}
fmt.Printf("%T\n", add) // func(int, int) int
fmt.Printf("%T\n", sub) // func(int, int) int
复制代码


参数


多个相邻类型的参数可以使用简写模式,所以刚才的 addsub 函数还可以这样写:


func add(x, y int) int {
  return x + y
}
func sub(x, y int) (z int) {
  z = x - y
  return
}
复制代码


支持不定参数,使用 ...type 语法。注意不定参数必须是函数的最后一个参数。


func funcSum(args ...int) (ret int) {
  for _, arg := range args {
    ret += arg
  }
  return
}
// 不定参数
fmt.Println(funcSum(1, 2))    // 3
fmt.Println(funcSum(1, 2, 3)) // 6
复制代码


也可以使用 slice 作为实参传入,需要使用 ... 将 slice 展开:


// slice 参数
s := []int{1, 2, 3, 4}
fmt.Println(funcSum(s...)) // 10
复制代码


其实,使用 slice 作为形参同样可以达到相同的效果,但区别就是传参的时候,必须要构造出来一个 slice 才行,没有不定参数使用起来方便。


func funcSum1(args []int) (ret int) {
  for _, arg := range args {
    ret += arg
  }
  return
}
fmt.Println(funcSum1(s))   // 10
复制代码


返回值


函数可以返回一个值,也可以返回多个值。


// 多返回值
func swap(x, y int) (int, int) {
  return y, x
}
// 多返回值
fmt.Println(swap(1, 2)) // 2 1
复制代码


如果有不需要的返回值,使用 _ 将其忽略:


x, _ := swap(1, 2)
fmt.Println(x) // 2
复制代码


支持命名返回值。使用命名返回值的话,直接使用 return 即可,后面不用跟返回值名。


前面不定参数的例子就是通过这种方式来写的:


func funcSum(args ...int) (ret int) {
  for _, arg := range args {
    ret += arg
  }
  return
}
复制代码


再来对比一下,如果不是采用命名返回值,应该怎么写:


func funcSum(args ...int) int {
  ret := 0
  for _, arg := range args {
    ret += arg
  }
  return ret
}
复制代码


匿名函数


匿名函数是指不需要定义函数名的一种函数实现方式。可以直接赋值给函数变量,可以当作实参,也可以作为返回值,还可以直接调用。


// 匿名函数
sum := func(a, b int) int { return a + b }
fmt.Println(sum(1, 2)) // 3
复制代码


作为参数:


// 匿名函数作为参数
func funcSum2(f func(int, int) int, x, y int) int {
  return f(x, y)
}
fmt.Println(funcSum2(sum, 3, 5)) // 8
复制代码


作为返回值:


// 匿名函数作为返回值
func wrap(op string) func(int, int) int {
  switch op {
  case "add":
    return func(a, b int) int {
      return a + b
    }
  case "sub":
    return func(a, b int) int {
      return a + b
    }
  default:
    return nil
  }
}
f := wrap("add")
fmt.Println(f(2, 4)) // 6
复制代码


直接调用:


// 直接调用
fmt.Println(func(a, b int) int { return a + b }(4, 5)) // 9
复制代码


总结


函数在之前的文章中已经使用过了,这篇再系统全面总结一下都有哪些需要注意的点。


包括函数定义,参数,返回和匿名函数。其实还有一个闭包,通过匿名函数来实现。但我感觉闭包使用的并不是很多,就没有写,感兴趣的同学可以自己搜搜看。


函数可以把复杂的程序分成更小的模块,使程序可读性更强,复用性更高,维护性更好。在开发过程中一定要具备将特定功能抽象成函数的能力,而不是将所有代码都写在一起,代码堆成一坨。这样的代码除了不好维护,重点是时间长了自己都不想看。



文章中的脑图和源码都上传到了 GitHub,有需要的同学可自行下载。


地址:github.com/yongxinz/go…



目录
相关文章
|
1月前
|
JSON 安全 网络协议
go语言使用内置函数和标准库
【10月更文挑战第18天】
20 3
|
7月前
|
存储 算法 Go
go语言中的延迟执行函数
【5月更文挑战第13天】`defer`是Go语言中用于延迟执行函数的关键字,尤其适用于资源管理,如文件关闭和锁的释放。它在函数返回前按照LIFO顺序执行,确保资源在任何返回路径下都能正确释放。`defer`可以拦截`panic`并在函数返回前执行,但无法阻止某些致命的`panic`。此外,`defer`可用于修改返回值、输出调试信息和还原变量值。尽管在某些场景下可能影响性能,但Go的优化使得其在多数情况下性能表现良好,特别是在资源清理方面。在Go 1.20及以后的版本,`defer`的性能已显著提升,尤其是在高计算量的场景下。
280 2
|
3月前
|
Go
go函数
go函数
36 10
|
3月前
|
编译器 Go C++
Go to Learn Go之函数
Go to Learn Go之函数
32 0
|
3月前
|
编译器 Go 索引
Go数组、多维数组和切片(动态数组),及常用函数len(),cap(),copy(),append()在切片中的使用
本文介绍了Go语言中数组、多维数组和切片(动态数组)的基本概念和操作,包括数组的定义、初始化、访问,多维数组的定义和访问,以及切片的创建、使用和扩容。同时,还讲解了切片中常用的函数len()、cap()、copy()和append()的使用方法。
|
4月前
|
Prometheus Cloud Native Go
Go 1.22 标准库 slices 新增函数和一些旧函数增加新特性
Go 1.22 标准库 slices 新增函数和一些旧函数增加新特性
|
4月前
|
安全 编译器 Go
Go 1.21: 泛型函数的全面回顾
Go 1.21: 泛型函数的全面回顾
|
4月前
|
Go
深入理解 Go 中的 new() 和 make() 函数
深入理解 Go 中的 new() 和 make() 函数
|
4月前
|
设计模式 Java 数据库连接
|
4月前
|
Go 开发者