go语言编程系列(四)

简介: go语言编程系列(四)

函数

Go语言函数式组织好的,可重复使用的,用于执行指定任务的代码块。Go语言中定义函数使用func关键字,具体格式如下:


func 函数名(参数)(返回值) {   函数体}

函数可以没有参数或接受多个参数

例如:


package basicimport "fmt"func Add(a, b int) int {   var ret = a + b   fmt.Printf("The sum of %d and %d is: %d\n", a, b, ret)   return ret}func Callback(y int, f func(int, int) int) {   f(y, 5) // this becomes Add(1, 5)}func DoCallback(y int) {   Callback(y, Add) // this becomes Add(1, 5)}

在本例中,add接受两个int类型的参数

注意:go和java不同,函数的返回类型,在函数名之后

当连续两个或者多个函数的已命名形参类型相同时,除最后一个类型以外,其他都可以省略。

在本例中


func Add(a int, b int) int {....}

其中的两个参数


a int, b int

被缩写为


a, b int

函数的定义:

函数构成代码执行的逻辑结构

除了main函数,返回值以及它们的类型被统称为函数签名


func function_name( [parameter list] ) [return_types] {   body(函数体)}

函数定义:

①、func:函数由func开始声明

②、function_name:函数名称,函数名和参数列表一起构成了函数签名

③、parameter list:参数列表,参数就像一个占位符,当函数被调用时,你可以将值传递给参数,这个值被称为实际参数,参数列表指定的是参数类型,顺序,及参数个数,参数是可选的,也就是说函数也可以不包含参数。

④、return_types:返回类型,函数返回列值,return_types是该列值的数据类型,有些功能不需要返回值,这种情况下return_types不是必须的。

⑤、body(函数体):函数定义的代码集合

形式参数列表描述了函数的参数名和参数类型

这些参数作为局部变量,其值由参数调用者提供,返回值列表描述了函数返回值的变量名以及类型

如果函数返回一个无名变量或者没有返回值,返回值列表的括号是可以省略的。

如果一个函数声明不包括返回值列表,那么函数体执行完毕,不会返回任何值。


// 这个函数计算两个int型输入数据的和,并返回int型的和func Add(a int, b int) int {  // Go需要使用return语句显式地返回值  return a + b}fmt.Println(Add(3,4)) //函数的调用

在Add函数中a和b是形参名 3和4是调用时的传入参数,函数返回了一个int类型的值

返回值也可以像形式参数一样被命名。

在这种情况下,每个返回值被声明成一个局部变量,并根据该返回值的类型,将其初始化为0.

如果一个函数在声明时,包含返回值列表,该函数必须以return语句结尾,除非函数明显无法运行到结尾处,例如函数在结尾处调用了panic异常或者函数中存在无限循环。

函数如何调用:

那么函数在使用的时候如何被调用呢?


package.Function(arg1, arg2, …, argn)

1、Function是package包里面的一个函数,括号里的是被调用函数的实参:这些值被传递给被调用函数的形参。

2、函数被调用的时候,这些实参将被复制然后传递给被调用函数。

3、函数一般是在其他函数里面被调用的,这个其它函数被称为调用函数。

4、函数能多次调用其他函数,这些被调用函数按顺序执行,理论上,函数调用其他函数的次数是无序的直到函数调用栈被耗尽。

函数的变长参数

在函数中有的时候可能也会遇到你要传递的参数的类型是多个(传递变长参数)如这样的函数


func patent(a,b,c ...int)

参数是采用...type的形式传递,这样的函数被称为变参函数

只有声明没有body的函数

你可能遇到没有声明函数体的函数声明,这表示该函数不是以go实现的

这样的声明定义了函数标识符


package mathfunc Sin(x float64) float //implemented in assembly language

返回多值的函数

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

swap函数返回了两个字符串


//函数定义func Swap(x, y string) (string, string) {  return y, x}//函数调用func main() {  basic.BasicTypeDemo()  basic.BasicTypeDemo()  basic.DoCallback(1)  basic.Callback(1, basic.Add)  a, b := basic.Swap("hello", "world")  fmt.Println(a, b)}

命名返回值

Go的返回值可被命名,它们会被视作定义在函数顶部的变量。

返回值的名称应当具有一定的意义,它可以作为文档使用。

没有参数的return语句,返回已命名的返回值,也就是直接返回


func Split(sum int) (x, y int) {  x = sum * 4 / 9  y = sum - x  return}

函数的使用


x, y := basic.Split(40)fmt.Println(x, y)

直接返回语句应当仅用在上面这样的短函数中,在长的函数中他们会影响代码的可读性。

匿名返回值

函数签名中命名返回值变量,只指定返回值类型,由return指定返回值


// 匿名返回值func Split02(sum int) (int, int) {  var x = sum * 4 / 9  var y = sum - x  return x, y}

任何一个非命名返回值在return语句里面都要明确指出包含返回值的变量或是一个可计算的值。

建议:尽量使用命名返回值:会使代码更清晰,更简短,同时更加容易读懂。

将函数作为参数传递

在Go语言中,函数是一等公民,也就是说,函数可以像值一样传递和使用。因此,可以将函数作为参数传递给其他函数,实现更加灵活的编程方式:


//类型:func(int, int) intfunc Add(a, b int) int {  var ret = a + b  fmt.Printf("The sum of %d and %d is: %d\n", a, b, ret)  return ret}func Callback(y int, f func(int, int) int) {   f(y, 5) // this becomes Add(1, 5)}  func DoCallback(y int) {   Callback(y, Add) // this becomes Add(1, 5)}

调用方法:


basic.DoCallback(1)basic.Callback(1, basic.Add)

输出结果:


The sum of 1 and 5 is: 6The sum of 1 and 5 is: 6

内置函数:

Go语言拥有一些不需要进行导入操作就可以使用的内置函数

它们有时可以针对不同的类型进行操作:例如,len,cap和append,或必须用于系统级的操作,例如:panic,因而,它们需要直接获得编译器的支持

a534c1bbb9099e3d8e611bc64fdaf3d1.png

内置接口:


type error interface {  //只要实现了Error()函数,返回值为String的都实现了err接口  Error() String}

递归函数:

当一个函数在其函数体内调用自身,则称之为递归

递归是一种强有力的技术特别是在处理数据结构的过程中。


// 递归函数定义func Processing(n int) (res int) {     if n <= 1 {     res = 1  } else {     res = Processing(n-1) + Processing(n-2)  }     return}  //调用  result := 0  for i := 0; i <= 10; i++ {  result = basic.Processing(i)  fmt.Printf("processing(%d) is: %d\n", i, result)}

输出结果:


processing(0) is: 1processing(1) is: 1processing(2) is: 2processing(3) is: 3processing(4) is: 5processing(5) is: 8processing(6) is: 13processing(7) is: 21processing(8) is: 34processing(9) is: 55processing(10) is: 89

在使用递归函数时经常会遇到一个重要的问题就是栈溢出:一般会出现在大量的递归调用导致的程序栈内存分配耗尽。

这个问题可以通过一个名为懒惰求值的技术解决,在go语言中,我们可以使用管道channel和goroutine也会通过这个方案来优化斐波那契数列的生成问题。

这样许多问题都可以使用优雅的递归来解决,比如说著名的快速排序算法

相关文章
|
20天前
|
存储 Go 索引
go语言中数组和切片
go语言中数组和切片
31 7
|
20天前
|
Go 开发工具
百炼-千问模型通过openai接口构建assistant 等 go语言
由于阿里百炼平台通义千问大模型没有完善的go语言兼容openapi示例,并且官方答复assistant是不兼容openapi sdk的。 实际使用中发现是能够支持的,所以自己写了一个demo test示例,给大家做一个参考。
|
20天前
|
程序员 Go
go语言中结构体(Struct)
go语言中结构体(Struct)
93 71
|
19天前
|
存储 Go 索引
go语言中的数组(Array)
go语言中的数组(Array)
100 67
|
22天前
|
Go 索引
go语言for遍历数组或切片
go语言for遍历数组或切片
91 62
|
24天前
|
并行计算 安全 Go
Go语言中的并发编程:掌握goroutines和channels####
本文深入探讨了Go语言中并发编程的核心概念——goroutine和channel。不同于传统的线程模型,Go通过轻量级的goroutine和通信机制channel,实现了高效的并发处理。我们将从基础概念开始,逐步深入到实际应用案例,揭示如何在Go语言中优雅地实现并发控制和数据同步。 ####
|
20天前
|
存储 Go
go语言中映射
go语言中映射
32 11
|
22天前
|
Go
go语言for遍历映射(map)
go语言for遍历映射(map)
31 12
|
21天前
|
Go 索引
go语言使用索引遍历
go语言使用索引遍历
28 9
|
25天前
|
安全 Serverless Go
Go语言中的并发编程:深入理解与实践####
本文旨在为读者提供一个关于Go语言并发编程的全面指南。我们将从并发的基本概念讲起,逐步深入到Go语言特有的goroutine和channel机制,探讨它们如何简化多线程编程的复杂性。通过实例演示和代码分析,本文将揭示Go语言在处理并发任务时的优势,以及如何在实际项目中高效利用这些特性来提升性能和响应速度。无论你是Go语言的初学者还是有一定经验的开发者,本文都将为你提供有价值的见解和实用的技巧。 ####