当前位置: 首页 > news >正文

golang学习笔记系列之函数

函数

golang函数简介

函数是go语言中的一级公民,我们把所有的功能单元都定义在函数中,可以重复使用。函数包含函数的名称,参数列表和返回值类型,这些构成了函数的签名。

golang中函数的特性

  • go语言中有3种函数:普通函数,匿名函数,方法(定义在struct上的函数)。
  • go语言中不允许函数重载(overload),也就是说不允许函数同名。
  • go语言中的函数不能嵌套函数,但可以嵌套匿名函数。
  • 函数是一个值,可以将函数赋值给变量,使得这个变量也成为函数。
  • 函数可以作为参数传递给另一个函数。
  • 函数的返回值可以是一个函数。
  • 函数调用的时候,如果有参数传递给函数,则先拷贝参数的副本,再将副本传递给函数。(值传递)
  • 函数参数可以没有名称。

golang中函数的定义和调用

函数在使用之前必须先定义,可以调用函数来完成某个任务。函数可以重复使用,从而达到代码重用。

语法

func function_name([parameter list])(return_types){
    //函数体
}
  • func:声明函数
  • function_name:函数名
  • [parameter list]:参数列表,参数就像一个占位符,当函数被调用时,你可以将值传递给函数,这个值被称为实际参数。参数列表指定的是参数类型,顺序及参数个数。参数是可选的,也就是说函数也可以不包含参数。
  • return_types:函数返回值的类型。
  • 函数体:函数的逻辑代码部分
//定义一个两数之和
func sum(a int,b int)(res int){
    res=a+b
    return res
}

golang函数的返回值

函数可以有0个或多个返回值,返回值需要指定数据类型,返回值通过return关键字来指定。

return可以有参数,也可以没有参数,这些返回值可以有名称,也可以没有名称。go中的函数可以有多个返回值。

  • return关键字中指定了参数时,返回值可以不用名称。如果return省略参数,则返回值部分必须带名称。
  • 当返回值有名称时,必须使用括号包围,逗号分隔,即使只有一个返回值。
  • 但即使返回值命名了,return中也可以强制指定其他返回值名称,也就是说return的优先级更高。
  • 命名的返回值是预先声明好的,在函数内部可以直接使用,无需再次声明。命名返回值的名称不能和函数参数名称相同,否则报错提示变量重复声明。
  • return中可以有表达式,但不能出现赋值表达式,这其他语言可能有所不同。例如可以:return a+b,但是不能:return a=b+c
package main

import "fmt"

//没有返回值
func foo1() {
	print("没有参数,也没有返回值!\n")
}

//有参数,没有返回值
func foo2(name string) {
	print("有一个参数:")
	fmt.Printf("name: %v\n", name)
}

//有参数,有返回值 且给返回值命名了
func foo3(name string, age int) (name2 string, age2 int) {
	name2 = name
	age2 = age
	return name2, age2
	// return    如果return不指定返回值,则默认返回上面定义的返回值
}

// 有参数,有返回值,但没有给返回值命名 这个时候就必须需要return来指定返回值
func foo4(name string, age int) (string, int) {
	return name, age

}

func main() {

	foo1()
	foo2("Tom")
	name2, age2 := foo3("Tom", 18)
	fmt.Printf("name2: %v\n", name2)
	fmt.Printf("age2: %v\n", age2)
	name, _ := foo4("Tom", 18)  //丢弃age
	fmt.Printf("name: %v\n", name)

}

运行结果

没有参数,也没有返回值!
有一个参数:name: Tom
name2: Tom
age2: 18
name: Tom

tips:

  • go中经常会使用其中一个返回值作为函数是否执行成功,是否有错误信息的判断条件。例如return value,exists;return value,ok(map中就是这样使用的);return value,err等
  • 当函数的返回值过多时,例如有4个以上的返回值,应该将这些返回值收集到容器中,然后以返回容器的方式去返回。
  • 当函数有多个返回值时,如果其中某个或几个返回值不想使用,可以通过下划线_来丢弃这些返回值。

golang函数的参数

go语言函数可以有0个或多个参数,参数需要指定数据类型。

声明函数时的参数列表叫做形参,调用时传递的参数叫做实参。

go语言是通过传值的方式传参的,意味着传递给函数的是拷贝后的副本,所以函数内部访问,修改的也是这个副本。(但是map,slice,interface,channel这些数据类型本身就是指针类型,所以就算是拷贝传值也是拷贝的指针,拷贝后的参数任然指向底层数据结构,所以修改它们可能会影响外部数据结构的值。)

go语言可以使用变长参数,有时候不能确定参数的个数时,可以使用变长参数,可以在函数定义语句的参数部分使用args…type的方式。这时会将…代表的参数全部保存到一个名为args的slice中,并且这些参数的数据类型都是type。

package main

import "fmt"

func test(a int) {
	a = 200
	fmt.Printf("里面的a: %v\n", a)
}

//两数之和
func sum(a int, b int) (c int) {
	c = a + b
	return
}

//...接收无数个参数
func foo(name string, age int, args ...string) {
	fmt.Printf("name: %v\n", name)
	fmt.Printf("age: %v\n", age)
	for _, v := range args {
		fmt.Printf("v: %v\n", v)
	}
}
func main() {

	a := 100
	test(a)
	fmt.Printf("外面的a=%v\n", a)

	res := sum(1, 2)
	fmt.Printf("res: %v\n", res)
	foo("Tom", 18, "Hello", "My", "name", "is", "Tom")

}

运行结果

里面的a: 200
外面的a=100
res: 3
name: Tom
age: 18
v: Hello
v: My
v: name
v: is
v: Tom

golang函数类型与函数变量

可以使用type关键字来定义一个函数类型,语法如下:

type fun func(par_type1,par_type2...) res_type1...
  • fun:自己定义的函数类型名
  • par_type1,par_type2…:表示各个参数的类型
  • res_type1…:表示各个返回值的类型
package main

import "fmt"

func sum(a int, b int) (c int) {
	c = a + b
	return
}

func main() {

	//自己声明函数的类型,然后再将函数赋值给一个变量
	type my_func func(int, int) int
	var my_sum my_func
	my_sum = sum
	res := my_sum(1, 2)
	fmt.Printf("res: %v\n", res)

	//直接通过短变量的形式将一个函数赋值给一个变量
	c := sum
	res2 := c(1, 2)
	fmt.Printf("res2: %v\n", res2)

}

运行结果

res: 3
res2: 3

高阶函数

go语言的函数,可以作为函数的参数,传递给另外一个函数,同时也可以作为函数的返回值返回。

函数作为参数和返回值

package main

import "fmt"

func sum(a int, b int) (c int) {
	c = a + b
	return
}

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

func test(a int, b int, f func(int, int) int) {
	res := f(a, b)
	fmt.Printf("%v+%v=res: %v\n", a, b, res)
}

func cal(flag string) func(int, int) int {
	switch flag {
	case "+":
		return sum
	case "-":
		return sub
	default:
		return sum
	}

}

func main() {

	//函数作为参数传给函数
	test(10, 20, sum)

	//函数作为返回值
	f_sum := cal("+")
	res := f_sum(1, 2)
	fmt.Printf("res: %v\n", res)

	f_sub := cal("-")
	res2 := f_sub(1, 2)
	fmt.Printf("res2: %v\n", res2)
}

运行结果

10+20=res: 30
res: 3
res2: -1

匿名函数

go语言函数不能嵌套,但是在函数内部可以定义匿名函数,实现一些简单的功能。所谓匿名函数,就是没有名称的函数。

语法

func ([参数列表])([返回值列表]){
    //函数体
}
package main

import "fmt"

func main() {

	//将匿名函数赋值给一个变量
	say_hello := func(name string, age int) {
		fmt.Printf("My name is  %v,And i',m %v old.\n", name, age)
	}
	say_hello("Tom", 18)

	//直接调用匿名函数
	func(words string) {
		fmt.Printf("words: %v\n", words)
	}("Hi,我是一个匿名函数!")

}

运行结果

My name is  Tom,And i',m 18 old.
words: Hi,我是一个匿名函数!

闭包

闭包就是延伸了函数作用于的函数,使得函数可以访问到函数体外的非全局变量。

package main

import "fmt"

//求平均值的闭包
func make_avger() func(int) float64 {

	s := make([]int, 0) 
    //对于下面的匿名函数来说,切片s就相当于函数体外的非全局变量
    //并且调用make_avager方法后,在全局中是访问不到切片s的
  

	return func(num int) float64 {
		s = append(s, num)
		sum := 0
		for _, v := range s {
			sum += v
		}
		return float64(sum / len(s))
	}

}
func main() {

	avg := make_avger()
	for i := 0; i < 10; i++ {
		fmt.Printf("%.2f\n", avg(i))

	}
}

运行结果

0.00
0.00
1.00
1.00
2.00
2.00
3.00
3.00
4.00
4.00

递归

函数内部调用函数自己的函数称为递归函数(同闭包一样,递归不是go语言特有的,而是go语言具备实现递归的条件)。

package main

//使用递归实现斐波那契数列
func fib(n int) int {
	if n == 1 || n == 2 {
		return 1
	} else {
		return fib(n-1) + fib(n-2)
	}
}

func main() {
	res := fib(10)
	print(res)
}

运行结果

102334155

同步更新于个人博客系统:golang学习笔记系列之函数

相关文章:

  • VJ_Dressing_思维
  • 关于我的vsc不能远程debug这件事
  • [English]英语积累本
  • java-php-python-ssm爱馨敬老院网站计算机毕业设计
  • 9.24 Day59---网络相关知识
  • [leetcode top100] 0924 找到数组中消失的数,合并二叉树,比特位计数,汉明距离
  • Java · 图书管理系统代码练习 · 抽象类 · 封装 · 继承 · 多态 · 接口
  • C语言 文件包含和条件编译
  • Vue入门【三】-- 详解computed计算属性
  • 线程池概念2
  • 一键部署设计稿至线上 —— D2C国产神器
  • 死锁检测实现
  • jsp杂草信息分类系统myeclipse开发sql数据库BS模式java编程网页结构serlvet技术mvc框架
  • 2022牛客多校(八)
  • jenkins 持续集成/项目部署
  • angular2开源库收集
  • ES2017异步函数现已正式可用
  • github指令
  • mongo索引构建
  • vue自定义指令实现v-tap插件
  • 笨办法学C 练习34:动态数组
  • 基于 Ueditor 的现代化编辑器 Neditor 1.5.4 发布
  • 将 Measurements 和 Units 应用到物理学
  • 那些被忽略的 JavaScript 数组方法细节
  • 前端
  • 世界上最简单的无等待算法(getAndIncrement)
  • 事件委托的小应用
  • 我从编程教室毕业
  • 正则学习笔记
  • 如何在 Intellij IDEA 更高效地将应用部署到容器服务 Kubernetes ...
  • 微龛半导体获数千万Pre-A轮融资,投资方为国中创投 ...
  • ​ 无限可能性的探索:Amazon Lightsail轻量应用服务器引领数字化时代创新发展
  • !!java web学习笔记(一到五)
  • #pragma预处理命令
  • $refs 、$nextTic、动态组件、name的使用
  • (2)STL算法之元素计数
  • (附源码)springboot宠物医疗服务网站 毕业设计688413
  • (附源码)计算机毕业设计SSM疫情下的学生出入管理系统
  • (每日持续更新)信息系统项目管理(第四版)(高级项目管理)考试重点整理 第13章 项目资源管理(七)
  • (三分钟)速览传统边缘检测算子
  • ./indexer: error while loading shared libraries: libmysqlclient.so.18: cannot open shared object fil
  • .net core webapi 大文件上传到wwwroot文件夹
  • .NET Core6.0 MVC+layui+SqlSugar 简单增删改查
  • .NET 的程序集加载上下文
  • .NET 中使用 TaskCompletionSource 作为线程同步互斥或异步操作的事件
  • .NET/C# 中你可以在代码中写多个 Main 函数,然后按需要随时切换
  • .net快速开发框架源码分享
  • .NET中的十进制浮点类型,徐汇区网站设计
  • .net中生成excel后调整宽度
  • @manytomany 保存后数据被删除_[Windows] 数据恢复软件RStudio v8.14.179675 便携特别版...
  • @Validated和@Valid校验参数区别
  • [ CTF ]【天格】战队WriteUp- 2022年第三届“网鼎杯”网络安全大赛(青龙组)
  • [ 渗透工具篇 ] 一篇文章让你掌握神奇的shuize -- 信息收集自动化工具
  • []常用AT命令解释()
  • [AMQP Connection 127.0.0.1:5672] An unexpected connection driver error occured