函数定义

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. 关于*.ashx 处理程序调试时 未能创建类型 错误

    出现改问题的根本原因是因为,我更改过改类型的名字,而IDE并没有更改  ***.ashx. 注册类型名字. 所更改的类的名字应该是  "项目名字.文件名字“ 例如下图 应该更改类为 ”Wx_ ...

  2. CSS3中的3D动画实现(钟摆、魔方)--实现代码

    CSS3中的3D动画实现(钟摆.魔方) transition-property 过渡动画属性  all|[attr] transition-duration 过渡时间 transition-delay ...

  3. querySelector与getElementBy的区别

    1,querySelector() 方法返回匹配指定 CSS 选择器元素的第一个子元素 . 该方法只返回匹配指定选择器的第一个元素.如果要返回所有匹配元素,需要使用 querySelectorAll( ...

  4. 2018-2019-2-20175303 实验一 《Java开发环境的熟悉》实验报告

    2018-2019-2-20175303 实验一 <Java开发环境的熟悉>实验报告 一.实验内容及步骤 实验1 1.用mkdir建立“20175303exp1”的目录 2.在“20175 ...

  5. LG3834 可持久化线段树1

    题意 给定\(N\)个整数构成的序列,将对于指定的闭区间查询其区间内的第\(K\)小值. $n \leq 2 \times 10^5 $ 思路 在\([l,r]\)区间内的数的个数,可以用\(sum[ ...

  6. yum解决 "Couldn't resolve host 'apt.sw.be'" 错误

    1.yum无法安装工具    failure: repodata/repomd.xml from dag: [Errno 256] No more mirrors to try.http://apt. ...

  7. 透过实现小型打包工具理解webpack

    面试经常有问到 webpack,偶遇一篇比较有实用价值的且有利于理解的文章,现总结如下: 本篇文章中要实现的这个迷你打包工具,它主要能实现如下两个功能: ①.将 ES6 转换成 ES5: ②.支持在 ...

  8. Spark学习笔记11面向对象编程

    面向对象编程   11.1 object类 11.1.1定义一个简单的类   11.1.2 field的getter与setter 定义类包含,定义类的field及方法.其格式如下 class Cla ...

  9. 2017(4)数据库系统,分布式数据库,NoSQL,反规范化

    试题四(共 25 分) 阅读以下关于数据库分析与建模的叙述,在答题纸上回答问题 1至问题 3. [说明] 某电子商务企业随着业务不断发展,销售订单不断增加,每月订单超过了 50 万笔,急需开发一套新的 ...

  10. python 小试一题

    a = 66count = 1while count <=3 : b = int(input("猜测这个数字:")) if b < a: print("猜测的 ...