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,则此变量的作用域仅限于此代码块

Golang入门学习(三):函数的更多相关文章

  1. SCARA——OpenGL入门学习三

    OpenGL入门学习[三] 在第二课中,我们学习了如何绘制几何图形,但大家如果多写几个程序,就会发现其实还是有些郁闷之处.例如:点太小,难以看清楚:直线也太细,不舒服:或者想画虚线,但不知道方法只能用 ...

  2. Golang入门学习(四):常用的函数汇总

    文章目录 2.4 常用的内置函数 2.4.1 字符串常用内置函数 2.4.2 常用的时间和日期相关函数 2.4.3 内置函数 2.4 常用的内置函数 2.4.1 字符串常用内置函数 https://g ...

  3. Golang入门学习(二):控制分支

    文章目录 @[TOC] 1. 控制分支 1.1 if-else分支 1.2 switch分支 1.4 while 和do...while循环结构 1.5 多种循环结构 1.6 break 1.7 co ...

  4. Oracle的基本学习(三)—函数

    一.字符函数   1.大小写控制函数 --lower:使字母变为小写-- --upper:使字母变为大写-- --initcap:使字符的第一个字母变为大写-- select lower('ABC') ...

  5. SpringMVC入门学习三

    今天是Springmvc学习的第三天,今天我将主要介绍一下: 常用注解的使用 关于非post.get请求的处理 文件上传与下载 拦截器   常用注解的使用 老大在此 @Controller @Cont ...

  6. Egg入门学习(三)---理解中间件作用

    Egg是基于koa的,因此Egg的中间件和Koa的中间件是类似的.都是基于洋葱圈模型的. 在Egg中,比如我想禁用某些IP地址来访问我们的网页的时候,在egg.js中我们可以使用中间件来实现这个功能, ...

  7. Golang入门学习(五):异常处理

    文章目录 2.5 错误处理机制 2.5.1 基本说明 2.5.2 入门示例 2.5.3 自定义错误 2.5 错误处理机制 2.5.1 基本说明 Go语言追求简洁优雅,因此并不支持传统的try-catc ...

  8. Python3.5入门学习记录-函数

    Python 函数 函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段. 函数能提高应用的模块性,和代码的重复利用率.你已经知道Python提供了许多内建函数,比如print().但你也 ...

  9. Struts入门学习(三)---自定义类型转换器

    类型转换器是将浏览器传递的参数进行转换为了与服务器端的参数匹配,先举个例子如果我们想往服务器传递日期类型的参数时我们要怎么让浏览器传过去的让服务器明白 我们新建一个类 ConverterTest.ja ...

随机推荐

  1. 论文笔记:(TOG2019)DGCNN : Dynamic Graph CNN for Learning on Point Clouds

    目录 摘要 一.引言 二.相关工作 三.我们的方法 3.1 边缘卷积Edge Convolution 3.2动态图更新 3.3 性质 3.4 与现有方法比较 四.评估 4.1 分类 4.2 模型复杂度 ...

  2. QT常用控件(三)——自定义控件封装

    引言 Qt已经提供了很多的基础控件供开发使用,而Qt原生的控件有时候并不能满足我们的需求,特别是在工业的运用上,比如我们需要一个日期时间的选择器,Qt虽然已经提供了原生的QDateTime控件,但这个 ...

  3. 为了彻底搞懂 hashCode,我钻了一下 JDK 的源码

    今天我们来谈谈 Java 中的 hashCode() 方法--通过源码的角度.众所周知,Java 是一门面向对象的编程语言,所有的类都会默认继承自 Object 类,而 Object 的中文意思就是& ...

  4. Haskell Command-line Application Building

    Haskeline Package Haskeline provides a user interface for line input in command-line programs. This ...

  5. Linux平台上转换文件编码

    Linux系统的iconv指令是一个很好的文件编码转换工具,支持的编码范围广,使用方便,例如将一个utf-8编码的文件(名为tic)转换为gbk编码: iconv -f utf-8 -t gbk ti ...

  6. Dubbo系列讲解之扩展点实现原理分析【2万字分享】

    Apache Dubbo 是一款微服务开发框架,它提供了 RPC通信 与 微服务治理 两大关键能力.这意味着,使用 Dubbo 开发的微服务,将具备相互之间的远程发现与通信能力, 同时利用 Dubbo ...

  7. Android系统编程入门系列之服务Service中的进程间通信

    在上篇文章以线程间的通信方式Handler类结尾,服务Service还支持的进程间通信,又是具体怎么实现的呢?这就要用到加载服务一文中提到的AIDL语言规范了. AIDL是 Android Inter ...

  8. 记面试的一道JS题

    给一个数组arr=[1,2,3,4,5],索引第二位插入'z',设计一个函数change,调用change(arr, 2, 'z')返回一个新数组[1,2,'z',3,4,5] 我想了两种办法: 第一 ...

  9. spring cloud alibaba版本选择

    https://github.com/alibaba/spring-cloud-alibaba/wiki/版本说明 Spring Cloud Version Spring Cloud Version ...

  10. docker加速器,设置cdn

    添加加速器 vim /etc/docker/daemon.json 添加如下内容 { "registry-mirrors": ["https://registry.doc ...