函数定义

Go语言中定义函数使用func关键字。

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

函数名:由字母、数字、下划线组成。但函数名的第一个字母不能是数字。在同一个包内,函数名也称不能重名(包的概念详见后文)。
参数:参数由参数变量和参数变量的类型组成,多个参数之间使用,分隔。
返回值:返回值由返回值变量和其变量类型组成,也可以只写返回值的类型,多个返回值必须用()包裹,并用,分隔。
函数体:实现指定功能的代码块。

函数定义及调用

//定义一个不需要参数也没有返回值的函数
func say(){
fmt.Println("Hello!")
}
//定义个接受string类型的name参数
func say2(name string) {
fmt.Println("hello ",name)
}
func main(){
//函数调用
say() //Hello!
say2("Ares") //hello Ares
}

参数

参数简写

函数的参数中如果相邻变量的类型相同,则可以省略类型

func intsum(x ,y int)  {
//return x + y
fmt.Println(x+y)
}

intSum函数有两个参数,这两个参数的类型均为int,因此可以省略x的类型.

可变参数

可变参数是指函数的参数数量不固定。Go语言中的可变参数通过在参数名后加...来标识。

func intsum2(x ... int) int {
fmt.Println(x)
sum := 0
for _,v :=range x{
sum += v
}
return sum
} func main(){
//函数调用
ret1 := intsum2() //[]
ret2 := intsum2(2) //[2]
ret3 := intsum2(3,3,3,3,3) //[3 3 3 3 3]
fmt.Println(ret1,ret2,ret3) //0 2 15
}

固定参数搭配可变参数时,可变参数要放固定参数后面。

func intsum3(x int,y ... int)int  {
fmt.Println(x,y)
sum := x
for _,v :=range y{
sum += v
}
return sum
} func main(){
//函数调用
ret4 := intsum3(10) //10 []
ret5 := intsum3(10,10) //10 [10]
ret6 := intsum3(10,11,12,13) //10 [11 12 13]
fmt.Println(ret4,ret5,ret6) //10 20 46
}

函数的可变参数是通过切片来实现的!

多返回值

函数如果有多个返回值时必须用()将所有返回值包裹起来。

//多返回值
func many(x,y int)(int, int) {
sum := x+y
sub := x-y
return sum,sub
} func main(){
//函数调用
ret7,ret8 := many(2,3)
fmt.Println(ret7,ret8) // 5 -1
}

函数定义时可以给返回值命名,并在函数体中直接使用这些变量,最后通过return关键字返回。return后的数据必须和函数定义的一致:个数、类型、顺序。
可以使用"_"来舍弃多余的返回值。
函数没有定义返回值也可使用return,用来结束函数执行(之后代码不再执行).

//多返回值命名
func many1(x,y int)(sum,sub int) {
sum = x+y
sub = x-y
return
}
func main(){
//函数调用
ret7,ret8 := many1(2,3)
fmt.Println(ret7,ret8) // 5 -1
}

defer语句

Go语言中的defer语句会将其后面跟随的语句进行延迟处理。在defer归属的函数即将返回时,将延迟处理的语句按defer定义的逆序进行执行,也就是说,先被defer的语句最后被执行,最后被defer的语句,最先被执行。先进后出,后进先出。

func main(){
fmt.Println("start")
defer fmt.Println(1)
defer fmt.Println(2)
defer fmt.Println(3)
fmt.Println("end")
}
输出:
start
end
3
2
1

defer语句能非常方便的处理资源释放问题!

函数进阶

全局变量

全局变量是定义在函数外部的变量,它在程序整个运行周期内都有效。 在函数中可以访问到全局变量。

//定义全局变量
var num int = 10 func test() {
fmt.Printf("num=%d\n",num)
}
func main() {
test() //num=10
}

局部变量

如果局部变量和全局变量重名,优先访问局部变量!

//定义全局变量
var num int = 10 //函数内定义局部变量
func test1() {
var num1 int = 20
fmt.Printf("num=%d,num1=%d\n",num,num1)
}
//语句块内定义
func test2(x,y int) {
fmt.Println(x,y) //函数参数只在本函数中有效 2 1
if x > y{
z := 1 //z只在if语句中有效
fmt.Println(z) //1
}
}
func main() {
test1() //num=10,num1=20
test2(2,1)
}

递归函数

一个函数自己调用自己,就是递归函数。

//计算1+2+...+n的值
func sum(n int) int {
if n == 1 {
return 1
}
return sum(n-1) + n
} //斐波那契数列(Fibonacci sequence)
func fei(n int) int {
if n == 1 || n == 2 {
return 1
}
return fei(n-1) + fei(n-2)
}
func main() {
fmt.Println(sum(5))
fmt.Println(fei(12))
}

函数作为变量

func test()  {
fmt.Printf("num=%d\n",num)
}
func main() {
aa := test //将函数test内存地址赋值给aa,不能使用test(),test()是调用函数代码,将返回值赋值给aa
fmt.Printf("%T\n",aa) //func()
aa() //num=10
}

函数作为参数(回调函数)

//函数作为参数
func add(x,y int) int {
return x + y
}
func sub(x,y int)int {
return x-y
}
//op是函数名,接受两个int类型参数,返回一个int类型
func calc(x,y int,op func(int,int) int) int {
return op(x,y)
}
func main() {
ret := calc(10,20,add)
fmt.Println(ret) //30
ret1 := calc(10,20,sub)
fmt.Println(ret1) //-10
}

参数传递

值传递:传递的是数据的副本、修改数据,对源数据没有影响,值类型的数据,默认都是值传递

引用传递:传递的是数据的内存地址,多个变量指向同一块内存,引用类型的数据,默认都是引用传递。

func zhichuandi(arr2 [3]int) {
fmt.Println("函数中,数组数据:", arr2) //[1 2 3]
arr2[0] = 100
fmt.Println("函数中,数组数据更改后:", arr2) //[100 2 3]
} func yinyong(arr4 []int) {
fmt.Println("函数中,切片数组:", arr4) //[1 2 3]
arr4[0] = 100
fmt.Println("函数中,切片更改后:", arr4) //[100 2 3]
} func main() {
arr1 := [3]int{1, 2, 3}
fmt.Println("函数调用前,数组数据:", arr1) //[1 2 3]
zhichuandi(arr1)
fmt.Println("函数调用后,数组数据:", arr1) //[1 2 3]
fmt.Println("------------------")
arr3 := []int{1, 2, 3}
fmt.Println("函数调用前,切片数据:", arr3) //[1 2 3]
yinyong(arr3)
fmt.Println("函数调用后,切片数据:", arr3) //[100 2 3]
}

匿名函数

匿名函数就是没有函数名的函数,匿名函数多用于实现回调函数和闭包。
匿名函数直接在函数后使用()调用,通常只能使用一次,可以使用匿名函数赋值给变量,可以调用多次。
将匿名函数作为另一个函数的参数,即为回调函数。
将匿名函数作为另一个函数的返回值,形成闭包。
格式:

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

定义完匿名函数后直接执行:

func main()  {
//方式1
func(x,y int){
fmt.Println(x + y)
}(1,2) //3,定义完匿名函数后加()直接执行
}
//方式2
say := func(){
fmt.Println("匿名函数")
}
say() //匿名函数

匿名函数使用方式

在定义匿名函数时就直接调用

//定义匿名函数的同时调用
ret1 := func(n1,n2 int) int {
return n1 + n2
}(1,2) //将执行结果返回给ret1
fmt.Println("ret1=",ret1) //ret1= 3

将匿名函数赋给一个变量(函数变量),再通过变量来调用匿名函数

//把匿名函数赋值给a变量
a := func(n1,n2 int) int {
return n1 + n2
} //将函数地址赋值给a
//通过a来调用匿名函数
ret2 := a(1,3)
fmt.Println("ret2=",ret2) //ret2= 4

全局匿名函数

//把全局匿名函数赋值给一个全局变量
var (
//全局匿名函数 赋值给变量fun1
Fun1 = func(n1 int, n2 int) int {
return n1 * n2
}
)
func main() {
fmt.Println(Fun1(1,2)) //2
}

闭包

闭包=函数+引用环境
一个外层函数中,有内层函数,该内存函数中,会操作外层函数的局部变量(外层函数中的参数,或者外层函数中直接定义的变量),并且该外层函数的返回值就是这个内层函数。
这个内层函数和外层函数的局部变量,统称为闭包。
只要引用到外部变量的,就是闭包!

//闭包
//定义一个函数返回值是一个函数
func a() func() {
name := "ares"
return func(){
fmt.Println("hello ",name)
}
}
//方式2
func b(name string) func() {
return func(){
fmt.Println("hello ",name)
}
}
func main() {
r := a() //此时r就是一个闭包
r() //相当于执行了a函数背部的匿名函数
}
r2 := b("ares")
r2()

使用闭包做文件名后缀检测实例:

func makeSuffixFunc(suffix string) func(string) string {
return func(name string) string {
//若文件后缀是不是以suffix结尾
if !strings.HasSuffix(name, suffix) {
//如果不是,返回name + suffix
return name + suffix
}else{
//如果是,返回ok
return "ok"
}
return name
}
}
func main() {
jpgFunc := makeSuffixFunc(".jpg")
txtFunc := makeSuffixFunc(".txt")
fmt.Println(jpgFunc("test")) //test.jpg
fmt.Println(txtFunc("test.txt")) //ok
}

闭包实例:

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(10)
ret1 := x(10) //base=10+10=20
fmt.Println(ret1) //20
ret2 := y(30) //base=20-30
fmt.Println(ret2) //-10
}

内置函数

panic/recover

GO可以使用panic/recover模式来处理错误。 panic可以在任何地方引发,但recover只有在defer调用的函数中有效。
实例:

func a()  {
fmt.Println("func a")
}
func b() {
panic("func b")
}
func c() {
fmt.Println("func c")
}
func main() {
a()
b()
c()
}

执行到func b的时候会抛出异常,程序崩溃,可以使用recover将程序恢复

func a()  {
fmt.Println("func a")
}
func b() {
defer func() {
err := recover()
if err != nil{
fmt.Println("recover fun b")
}
}()
panic("func b")
}
func c() {
fmt.Println("func c")
}
func main() {
a() //func a
b() //recover fun b
c() //func c
}
  • recover()必须搭配defer使用。
  • defer一定要在可能引发panic的语句之前定义。

GO函数的更多相关文章

  1. Python 小而美的函数

    python提供了一些有趣且实用的函数,如any all zip,这些函数能够大幅简化我们得代码,可以更优雅的处理可迭代的对象,同时使用的时候也得注意一些情况   any any(iterable) ...

  2. 探究javascript对象和数组的异同,及函数变量缓存技巧

    javascript中最经典也最受非议的一句话就是:javascript中一切皆是对象.这篇重点要提到的,就是任何jser都不陌生的Object和Array. 有段时间曾经很诧异,到底两种数据类型用来 ...

  3. JavaScript权威指南 - 函数

    函数本身就是一段JavaScript代码,定义一次但可能被调用任意次.如果函数挂载在一个对象上,作为对象的一个属性,通常这种函数被称作对象的方法.用于初始化一个新创建的对象的函数被称作构造函数. 相对 ...

  4. C++对C的函数拓展

    一,内联函数 1.内联函数的概念 C++中的const常量可以用来代替宏常数的定义,例如:用const int a = 10来替换# define a 10.那么C++中是否有什么解决方案来替代宏代码 ...

  5. 菜鸟Python学习笔记第一天:关于一些函数库的使用

    2017年1月3日 星期二 大一学习一门新的计算机语言真的很难,有时候连函数拼写出错查错都能查半天,没办法,谁让我英语太渣. 关于计算机语言的学习我想还是从C语言学习开始为好,Python有很多语言的 ...

  6. javascript中的this与函数讲解

    前言 javascript中没有块级作用域(es6以前),javascript中作用域分为函数作用域和全局作用域.并且,大家可以认为全局作用域其实就是Window函数的函数作用域,我们编写的js代码, ...

  7. 复杂的 Hash 函数组合有意义吗?

    很久以前看到一篇文章,讲某个大网站储存用户口令时,会经过十分复杂的处理.怎么个复杂记不得了,大概就是先 Hash,结果加上一些特殊字符再 Hash,结果再加上些字符.再倒序.再怎么怎么的.再 Hash ...

  8. JS核心系列:浅谈函数的作用域

    一.作用域(scope) 所谓作用域就是:变量在声明它们的函数体以及这个函数体嵌套的任意函数体内都是有定义的. function scope(){ var foo = "global&quo ...

  9. C++中的时间函数

    C++获取时间函数众多,何时该用什么函数,拿到的是什么时间?该怎么用?很多人都会混淆. 本文是本人经历了几款游戏客户端和服务器开发后,对游戏中时间获取的一点总结. 最早学习游戏客户端时,为了获取最精确 ...

  10. Python高手之路【四】python函数装饰器

    def outer(func): def inner(): print('hello') print('hello') print('hello') r = func() print('end') p ...

随机推荐

  1. Golang 微框架 Gin 简介

    框架一直是敏捷开发中的利器,能让开发者很快的上手并做出应用,甚至有的时候,脱离了框架,一些开发者都不会写程序了.成长总不会一蹴而就,从写出程序获取成就感,再到精通框架,快速构造应用,当这些方面都得心应 ...

  2. AWS Nginx Started but not Serving AWS上Nginx服务器无法正常工作

    After install the Nginx on AWS instance, and visit your public ip address, you might see the followi ...

  3. pytorch入门与实践-3 Tensor详解

    1--如第二章所讲,Tensor的本质是矩阵或数据 2--对Tensor的操作分类 |----API分类 |------torch中定义的: t.f(a,b) |------tensor的成员函数: ...

  4. Linux学习笔记(三)

    昨天将计算机概论部分算是看完了,然后我又将后面的内容过了一遍,因为我本身对Linux有一些了解,所以就跳到这文件权限与目录管理配置这章了,话不多说,开始做笔记. Linux文件权限与目录配置 群组 所 ...

  5. 洛谷 P3379 【模板】最近公共祖先(LCA)

    题目描述 如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先. 输入输出格式 输入格式: 第一行包含三个正整数N.M.S,分别表示树的结点个数.询问的个数和树根结点的序号. 接下来N-1行每 ...

  6. css学习_css补充知识

    1.渐进增强,优雅降级 2.浏览器前缀 3.背景渐变   4.css  验证工具 2种方式:第2种支持验证本地的css(推荐) 5.css压缩  ----(节约空间,节省带宽) 6.旋转轮播图 案例: ...

  7. -ms、-moz、-webkit -o

    -ms-transform:rotate(7deg); //-ms代表ie内核 识别码-moz-transform:rotate(7deg); //-moz代表火狐内核识别码 -webkit-tran ...

  8. 滴滴出行基于RocketMQ构建企业级消息队列服务的实践

    小结: 1. https://mp.weixin.qq.com/s/v6NM3UgX-qTI7yO1QPCJrw 滴滴出行基于RocketMQ构建企业级消息队列服务的实践 原创: 江海挺 阿里巴巴中间 ...

  9. Kafka: Exactly-once Semantics

    https://www.confluent.io/blog/enabling-exactly-kafka-streams/ https://cwiki.apache.org/confluence/di ...

  10. Git branch && Git checkout常见用法

    https://www.cnblogs.com/qianqiannian/p/6011404.html git branch 和 git checkout经常在一起使用,所以在此将它们合在一起 1.G ...