Go语言基础——03 函数

Go语言基础——03 函数

函数

计算机的函数,是一个固定的一个程序段,或称其为一个子程序,它在可以实现固定运算功能的同时,还带有一个入口和一个出口,所谓的入口,就是函数所带的各个参数,我们可以通过这个入口,把函数的参数值代入子程序,供计算机处理;所谓出口,就是指函数的函数值,在计算机求得之后,由此口带回给调用它的程序。

  • 函数是基本的代码块。
  • Go语言中至少要有一个 main 函数。

函数的基本定义格式:

func functionName([parameter list]) [returnTypes] {函数体}
函数定义解析:

  • func:函数由 func 开始声明
  • functionName:函数名称
  • parameter list:参数列表
  • returnTypes:返回类型
  • 函数体:一段代码的集合
    注意:函数和方法是完全不一样的,只有面向对象里才有方法。而Go是一门静态强类型、编译型语言。

函数签名

函数参数、返回值以及它们的类型被统称为函数签名

函数的调用

函数被调用的基本格式如下:
packageName.Function(arg1, arg2, ..., argn)

FunctionpackageName 包里面的一个函数,括号里的是被调用函数的实参(argument):这些值被传递给被调用函数的形参(parameter)。

函数的使用

  • 无参无返回值的函数
  • 有一个参数的函数
  • 有两个参数或多个参数的函数
  • 有一个返回值的函数
  • 有两个或多个返回值的函数
package main

import "fmt"

func main() {
	outputStatement()

	outputParameter("这是一个参数的函数")

	outputParameters("ZhangSan", 18)

	fmt.Println(addSum(1, 2))

	fmt.Println(returnValues("返回值1", "返回值2"))
}

// 无参无返回值
func outputStatement() {
	fmt.Println("这是一个无参无返回值的函数")
}

// 有一个参数
func outputParameter(str string) {
	fmt.Println(str)
}

// 有两个或多个参数
func outputParameters(name string, age int) {
	fmt.Println("输出参数1:", name, "输出参数2:", age)
}

// 有一个返回值
func addSum(num1 int, num2 int) int {
	return num1 + num2
}

// 有两个或多个返回值
func returnValues(value1 string, value2 string) (string, string) {
	return value1, value2
}
  • 可变参数(长参数)

在参数类型确定,但参数个数不确定时,我们可以使用可变参数,可变参数如果有多个参数时,必须放在最后一个参数

package main

import "fmt"

func main() {
	sum := getSum(1, 2, 3, 4, 5, 6)
	fmt.Println(sum)
}

func getSum(nums ...int) int {
	sum := 0
	for _, num := range nums {
		sum += num
	}
	return sum
}

注意:
在Go语言中没有函数重载(函数重载:指函数名相同,参数或返回值不同的函数),在Go语言中函数的重载是不允许的,会导致编译错误,Go不支持函数重载的主要原因是:函数重载需要进行多余的类型匹配,会影响性能。

  • 命名的返回值
package main

import "fmt"

func main() {
	x1, x2 := MultReturnVal(1, 2)
	fmt.Printf("和: %d, 积: %d", x1, x2)
}

func MultReturnVal(a int, b int) (x1 int, x2 int) {
	x1 = a + b
	x2 = a * b
	return
}

命名返回值作为结果形参被初始化为相应类型的零值,当需要返回的时候,我们只需要不带参数的 return 语句即可。需要注意,即使只有一个命名返回值,也需要用()括起来。

  • 改变外部变量

传递指针给函数不但可以节省内存(因为没有赋值变量的值),而且赋予了函数直接修改外部变量的能力,所以被修改的变量不再需要使用return返回。
当需要在函数内改变一个占用内存比较大的变量时,性能优势就会非常明显。在使用可以改变外部变量的函数时,需要添加注释,便于他人能了解此函数。

package main

import "fmt"

func main() {
	n := 0
	// 类型: int, 地址: 0xc00000e0c8, 值: 0
	fmt.Printf("类型: %T, 地址: %p, 值: %d\n", n, &n, n)

	reVal := &n
	// 类型: *int, 地址: 0xc00000e0c8, 值: 0
	fmt.Printf("类型: %T, 地址: %p, 值: %d\n", reVal, reVal, *reVal)

	returnVal(2, 3, reVal)
	// 类型: *int, 地址: 0xc00000e0c8, 值: 6
	fmt.Printf("类型: %T, 地址: %p, 值: %d\n", reVal, reVal, *reVal)
}

func returnVal(a, b int, reVal *int) {
	*reVal = a * b
}

递归函数

一个函数在其函数体内调用自身,称为递归。
注意:
递归函数需要有一个出口,逐渐向出口靠近,没有出口就会形成死循环。

经典的例子:斐波那契数列
即:前两个数为1,从第三个数开始每个数均为前两个数之和

package main

import "fmt"

func main() {
	result := 0
	for i := 1; i <= 10; i++ {
		result = fibonacciSequence(i)
		fmt.Printf("斐波那契数列(%d): %d\n", i, result)
	}
}

func fibonacciSequence(n int) (returnVal int) {
	if n <= 2 {
		returnVal = 1
	} else {
		returnVal = fibonacciSequence(n-1) + fibonacciSequence(n-2)
	}
	return
}

/*
结果:
斐波那契数列(1): 1
斐波那契数列(2): 1
斐波那契数列(3): 2
斐波那契数列(4): 3
斐波那契数列(5): 5
斐波那契数列(6): 8
斐波那契数列(7): 13
斐波那契数列(8): 21
斐波那契数列(9): 34
斐波那契数列(10): 55
*/

求和

package main

import "fmt"

func main() {
	val := summation(5)
	fmt.Println(val) // 15
}

/*
summation(5) = summation(4) + 5 = 15
summation(4) = summation(3) + 4 = 10
summation(3) = summation(2) + 3 = 6
summation(2) = summation(1) + 2 = 3
summation(1) = 1
*/
func summation(num int) (returnVal int) {
	if num == 1 {
		returnVal = 1
	} else {
		returnVal = summation(num-1) + num
	}
	return
}

defer

关键字 defer,允许推迟到函数返回之前一刻才执行某个语句或函数。
defer的用法类似面向对象编程语言的Java和C#的finally语句块,它一般用于释放某些已分配的资源。

package main

import "fmt"

/*
执行 -- fun1 -- start
执行 -- fun1 -- end
执行 -- fun2
*/
func main() {
	fun1()
}

func fun1() {
	fmt.Println("执行 -- fun1 -- start")
	defer fun2()
	fmt.Println("执行 -- fun1 -- end")
}

func fun2() {
	fmt.Println("执行 -- fun2")
}
package main

import "fmt"

/* 结果:
	1
	3
	4
	6
	7
	8
	10
	9
	5
	2
*/
func main() {
	n := 2

	fmt.Println("1")
	defer fmt.Println(n)
	fmt.Println("3")
	fmt.Println("4")
	defer fmt.Println("5")
	fmt.Println("6")
	fun(7)
	fmt.Println("8")
	defer fun(9)
	fmt.Println("10")
}

func fun(a int) {
	fmt.Println(a)
}
  • 在函数中可以添加 多个defer语句,这些defer语句会按照逆序执行(类似栈,即后进先出)
  • defer存在参数传递

回调函数

函数作为其它函数的参数进行传递,然后在其它函数内调用执行,称为回调函数。

package main

import "fmt"

func main() {
	resultAdd := oper(1, 2, add)
	resultSub := oper(3, 2, sub)
	fmt.Println(resultAdd)
	fmt.Println(resultSub)
}

func oper(a, b int, fun func(int, int) int) (result int) {
	result = fun(a, b)
	return
}

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

func sub(a, b int) (result int) {
	result = a - b
	return
}

匿名函数

匿名函数:顾名思义,没有名字的函数。

package main

import "fmt"

// Fun 定义一个全局匿名函数
var Fun = func(a, b int) (res int) {
	res = a * b
	return
}

func main() {
	// 定义一个匿名函数并调用
	func(s string) (str string) {
		str = s
		fmt.Println(str)
		return
	}("这是一个匿名函数")

	// 将一个匿名函数赋值给一个变量,通过变量来调用匿名函数
	result := func(a, b int) (res int) {
		res = a + b
		return
	}
	fmt.Println(result(1, 2))

	// 调用全局匿名函数
	res := Fun(2, 3)
	fmt.Println(res)
}

闭包

一个外层函数中,有内层函数,该内层函数中,会操作外层函数的局部变量,并且该外层函数的返回值就是这个内层函数。这个内层函数和外层函数的局部变量,统称为闭包结构。

package main

import "fmt"

// 用 闭包 生成 斐波那契数列
/* 结果
	1
	1
	2
	3
	5
	8
	13
	21
	34
	55
*/
func main() {
	f := fibonacci()
	for i := 1; i <= 10; i++ {
		fmt.Println(f())
	}
}

func fibonacci() func() (res int) {
	var a, b int = 1, 1
	return func() (res int) {
		res = a
		a, b = b, a+b
		return
	}
}

注意:

  • 在闭包结构中,局部变量的生命周期会发生改变,正常局部变量会随函数的调用而创建,随函数的结束而销毁,但在闭包结构中,因为内层函数还在使用外层函数的局部变量,所以外层函数的局部变量不会随着外层函数的结束而销毁。
  • 闭包结构可能会造成内存泄漏,因为垃圾回收不会将闭包结构中的变量销毁。
  • 通过闭包结构创建的函数内部变量,只在这个函数中作用,不会和其它函数冲突。
  • 闭包结构的返回值是一个函数,这个函数可以调用闭包结构中的变量。
  • 闭包结构可以解决全局变量污染问题。

计算函数执行时间

package main

import (
	"fmt"
	"time"
)

func main() {
	start := time.Now()
	result := funTime(10000)
	fmt.Println(result)
	end := time.Now()
	delta := end.Sub(start)
	fmt.Printf("函数执行需要的时间: %s\n", delta)
}

func funTime(a int) (result []int) {
	for i := 0; i < a; i++ {
		result = append(result, i)
	}
	return
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值