Golang入门学习(三):函数
文章目录
2.3 函数
2.3.1 基本语法
func 函数名(形参列表) (返回值列表){
执行语句... ...
return 返回值列表
}
- Go语言中支持函数返回多个值
- 如果返回值只有一个,则(返回值列表)的括号可以不写
- 如果有多个返回值,可以使用’_'来表示不处理此返回值
2.3.2 入门demo:
package main
import "fmt"
/*返回两个整数的和*/
/*函数只有一个返回值*/
func getSum(n1 int, n2 int) int {
return n1 + n2
}
/*返回有两个返回值*/
func getSumSub(n1 int, n2 int) (sum, sub int) {
return n1 + n2, n1 - n2
}
func main() {
fmt.Println(getSum(10, 23))
fmt.Println(getSumSub(10, 23))
sum, _ := getSumSub(123, 543) /*使用下划线来忽略此两者之差的返回值*/
fmt.Println(sum)
}
2.3.3 函数递归:
- 示例一:使用斐波那契数数列来说明:
/*1 1 2 3 5 8 13 21 34 55*/
func fabnacci(n int) int {
if n == 1 || n == 2 {
return 1
} else if n > 2 {
return fabnacci(n-1) + fabnacci(n-2)
}
return 0
}
- 示例二:猴子吃桃问题:
/*猴子吃桃问题:有一堆桃子,猴子第一天吃了其中的一半并再多吃一个;以后每天吃一半再多吃一个,当第10天时,只剩下一个桃子
* 问:最开始有多少桃子?
*/
func monkeyPeach(day int) int {
if day == 10 {
return 1
} else if day < 10 && day > 0 {
return (monkeyPeach(day+1) + 1) * 2
}
fmt.Println("输入的天数有错误!!!")
return -1
}
2.3.4 函数注意事项
- 函数的形参列表和返回值列表都可以是多个
- 如果有多个返回值,可以使用’_'来表示不处理此返回值
- 形参列表和返回值列表可以是值类型也可以是引用类型
- 函数名应该遵循相应的命名规范
- 如果需要导出供其他包调用,首字母需要大写…
- 函数传参是值传递,即将原参数拷贝一份传递给形参
- 支持传递指针(本质还是值传递),即传输变量地址
- GO不支持函数重载(函数名相同,但是形参不同)
- 在Go中函数也是一种数据类型,可以赋值给一个变量,这种情况下此变量便是一个函数类型的变量。可以通过此变量对函数进行调用
- 函数既然是一种函数类型,那么函数的形参列表和返回值列表都可以使用函数类型
func getSum(n1 int, n2 int) int {
return n1 + n2
}
func myFun(funcPtr func(int, int) int, num1 int, num2 int) int {
return funcPtr(num1, num2)
}
func main(){
fmt.Println(myFun(getSum, 10, 20))
}
- GO中支持自定义数据类型
基本语法:type 自定义数据类型名称 数据类型
例子:type age int
type getSum func(int,int) int
- GO中支持对返回值命名
func getSumSub(x,y int)(sum int, sub int){
sum = x + y
sub = x - y
return
}
- GO函数支持可变参数,可变参数必须是形参的最后一个。可变参数实际上是切片类型,可以通过索引访问
func getSum(sum int, args...int) int {
for i:=0;i<len(args);i++{/*遍历可变参数args*/
sum+=args[i]
fmt.Println(args[i])
}
return sum
}
2.3.5 init函数
每一个源文件都可以有一个init函数,它在main函数之前被Go框架调用。可以在init函数中完成初始化工作
注意事项:
- 如果一个文件同时包含全局变量定义、init函数、main函数,则执行顺序为: 自定义变量—> init函数 --> main函数
var age = test() func test() int {
fmt.Println("-----test函数初始化-------")
return 10
} func init() {
fmt.Println("----init函数初始化--------")
} func main() {
fmt.Println("--------main函数-------")
}
/*执行结果如下:*/
-----test函数初始化-------
----init函数初始化--------
--------main函数-------
- 如果import的其他包中也有变量定义,init函数,那么他们的执行顺序是:先import其他包的定义、init,然后本包的定义,init
2.3.6 匿名函数
Go支持匿名函数。如果某一个函数我们只是用一次,则可以考虑使用匿名函数。但是匿名函数可以调用多次
匿名函数用法:
- 定义匿名函数时直接调用,这种匿名函数只能调用一次
func main() {
rest1 := func(n1 int, n2 int) int {
return n1 + n2
}(10, 20)
fmt.Println(rest1)
}
- 将匿名函数赋值给变量,通过变量调用匿名函数。此时可以条用多次
func main() {
a := func(n1 int, n2 int) int {
return n1 + n2
}
rest := a(2, 3)
fmt.Println(res)
}
2.3.7 闭包
1)概念介绍:
- 闭包就是一个函数及其相关的引用环境组成的一个整体
2)示例:
/* 函数闭包 : 累加器函数*/
func addUpper() func(int) int {
var n int = 10
return func(x int) int {
n += x
return n
}
}
func main() {
f := addUpper() //返回一个函数
fmt.Println(f(1)) //11
fmt.Println(f(2)) //13
fmt.Println(f(3)) //16
}
3)上述代码说明:
addUpper()是一个函数,它的返回值是一个函数:
func(int)int
闭包的说明:
var n int = 10 return func(x int) int {
n += x
return n
}
返回一个匿名函数。但是这个匿名函数引用到函数外的n, 因此这个匿名函数就和n形成一个整体,构成闭包
大家可以这样理解:闭包是一个类,匿名函数是操作,n是字段。函数和它使用到的n构成闭包
当我们反复调用f函数时,n只初始化一次,因此每次调用一次,n就会累加一次
理解的关键:返回的函数与它引用的外部变量组成闭包
4)闭包的最佳实践:
编写一个函数makeSuffix(suffix string),可以接受一个文件的后缀名(如.jpg),并返回一个闭包
调用闭包,可以传入一个文件名,如果文件名没有后缀,则返回文件名.jpg;如果有文件名则返回文件名
要求使用闭包完成
strings.HasSuffix 可以用来判断某一个字符串是否包含指定后缀
func makeSuffix(suffix string) func(string) string {
return func(name string) string {
if !strings.HasSuffix(name, suffix) {
return name + suffix
}
return name
}
func main(){
f2 := makeSuffix(".jpg")
fmt.Println(f2("123")) //
fmt.Println(f2("456.avi")) //
fmt.Println(f2("789")) //
f3 := makeSuffix(".avi")
fmt.Println(f3("f:\\golang")) //
fmt.Println(f3("f:\\golang\\Go_han")) //
fmt.Println(f3("f:\\golang\\Go_han\\chapter06")) //
}
}
2.3.8 defer
1)为什么需要defer
在函数中往往需要申请框架各种资源(如文件句柄、锁、套接字等),为了在函数执行完毕后,能及时释放资源,Go语言提供了defer延时处理机制。**
2)快速入门案例
func sum(n1 int, n2 int) int {
defer fmt.Println("n1 =", n1)
defer fmt.Println("n2 =", n2)
n1++
n2++
ret := n1 + n2
fmt.Println("sum = ", ret)
return ret
}
func main() {
sum(1, 2)
}
运行结果如下:
sum = 3
n2 = 2
n1 = 1
3) defer注意事项
- defer在函数的return之后运行
- defer是一个栈,先定义的defer后执行
- defer也会将变量压入栈中
func sum(n1 int, n2 int) int {
defer fmt.Println("n1 =", n1)
defer fmt.Println("n2 =", n2) n1++
n2++ ret := n1 + n2
fmt.Println("sum = ", ret)
return ret
}
func main() {
sum(1, 2)
}
执行结果如下:
sum = 5
n2 = 2
n1 = 1
- 在Go中defer通常的用法是:创建资源后,执行defer语句来延时释放资源
- defer之后可以继续使用该资源
- 在函数执行完毕后,系统会依次从defer栈中取出语句,并执行之
- defer机制简洁高效
2.3.9 函数参数传递方式
1)基本介绍
函数参数传递通常有两种方式:
- 值传递
- 基本数据类型,如int系列、float系列、bool、string、数据、结构体struct
- 引用传递
- 指针、切片、map、管道chan、接口interface等都是引用数据类型
个人感觉这两种是一种传递方式,都是值传递。不同的是一个传递变量的副本,另一个传递的是变量地址的副本。
Go严格的说与C相同,不支持引用传递。但是通常情况下,将地址传递称之为引用传递(效果上类似),所以就沿用这种说法吧
2.3.10 变量作用域
- 函数内部声明/定义的变量,称之为局部变量,作用域仅限于函数内部
- 函数外部声明/定义的变量,称之为全局变量,作用域在整个包都有效,如果其首字母为大写,则作用域在整个程序都有效
- 如果变量在一个代码块中定义,如for/if,则此变量的作用域仅限于此代码块
最新文章
- mvp+retrofit+rxjava
- iOS常用设计模式笔记
- Ubuntu 14.10下搭建简易FTP服务器[vsftpd]
- Error while performing database login with the sqljdbc driver:Unable to create connection. Check your URL.
- Mysql 中有关日期的函数(sql)
- Ramsey theorem in Combinarotics
- dubbo源码之四——服务发布二
- Cheatsheet: 2014 01.01 ~ 01.14
- 高斯混合模型参数估计的EM算法
- onNewIntent调用时机
- 用绝对路径引用JS、CSS
- How to disable Eclipse splash
- QQ音乐的请求
- 自学Python3.5-字符串格式化 作用域 递归
- 定时器NSTimer
- Android SDK4/5/6/7,相册、拍照及裁剪功能及遇见的坑
- CSS的50个代码片段
- Hadoop生产环境搭建(含HA、Federation)
- 如何清理休眠文件(hiberfil.sys)
- Java-idea-FindBugs、PMD和CheckStyle对比