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 语言使用的是值传递,即在调用过程中不会影响到实际参数

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

4.1 值类型

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

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

4.2 引用类型

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
113的余数: 2
取整数,取小数
5 0.2599999999999998
32次方 9
104次方 10000
8的开平方 2.8284271247461903
8的开立方 2
圆周率 3.141592653589793

6. 回调函数

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

函数执行流程描述
① 首先在main函数中调用了test1()函数,这时候test1会顺着去加载里面的语句 1、2,直到语句2加载完后,加载回调函数test2test2会执行函数体内的语句 1、2,3
② 当test2函数体中语句执行完成,回调函数的生命周期将结束,如果有返回值将返回,没有则结束;
③ 当回调函数执行完以后,将继续执行test1函数中的语句 3,4,执行完以后test1函数的生命周期也将结束
test1函数结束以后就会返回main主函数

在这里插入图片描述

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

在这里插入图片描述

示例 ②

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

9. 函数方法

  • Go 语言中同时有函数和方法
  • 一个方法就是一个包含了接受者的函数,接受者可以是命名类型或者结构体类型的一个值或者是一个指针
  • 所有给定类型的方法属于该类型的方法集。

语法格式如下:

func (variable_name variable_data_type) function_name() [return_type]{
   /* 函数体 */
}

定义一个结构体类型和该类型的一个方法:

package main

import (
	"fmt"
	"math"
)

//定义结构体
type Circle str    uct {
	radius float64 //定义半径类型
}

func main() {
	var c1 Circle
	c1.radius = 5
	fmt.Println("圆的面积=", c1.getArea())
}

//该方法属于Circle类型对象中的方法
func (c Circle) getArea() float64 {
	//c.radius 是Circle类型对象中的属性
	return math.Pi * c.radius * c.radius
}


//输出结果如下
圆的面积= 78.53981633974483

总结

  • 闭包的本质:
    本质上依旧是一个匿名函数,只不过这个函数引入外界的变量\参数
    匿名函数+引用的变量=闭包

  • 闭包的特点:
    ① 返回的是一个匿名函数,但是这个函数引用到函数体外的变量/参数,因此这个匿名函数就和变量/参数形成了一个整体,构成了闭包
    ② 闭包中使用的变量/参数会一直保存在内存中,所以会一直使用,意味着闭包不可滥用(对内存消耗大)

  • 闭包的应用场景:
    闭包可以保留上次引用的某个值,我们传入一次就可以反复的进行使用了

  • 函数方法:
    假如定义了一个名为a的结构体,结构体内定义了他的属性或者变量申明,然后我定义了他的方法(一个或多个方法集),这些个方法都只属于结构体a,并且只能被a所调用,别的函数都没法调用,这个我们就叫他为函数方法。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

头发莫的了呀

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值