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. "virtualbox for mac安装器遇到了一个错误, 导致安装失败. 请联系软件制造商以获得帮助"的解决办法

    virtualbox下载地址: https://www.virtualbox.org/wiki/Downloads 原因:"Mac OS 10.13.4 会阻止外部内核扩展的安装" ...

  2. 20初识前端HTML(1)

    1 .HTML 1.1 网页的组成 文字 图片 链接 等元素构成.除了这些元素之外 网页中还可以包含音频 视频 等 1.2 WEB前端开发的流程 现在主流的开发流程: 前后端分离的开发模式. 美工:p ...

  3. TCP可靠传输

    1. TCP 可靠性如何保证? 信道可靠:用三次握手.四次挥手保证连接正确: 数据正确:分区编号.校验和.超时重传: 传输控制:流量控制.拥塞控制 2. 重传机制 TCP可靠传输方式是序列号与确认应答 ...

  4. Apache网页优化与安全

    目录 一.Apache网页优化 1.1.概述 1.2.gzip介绍 1.3.Apache的压缩模块 二.网页压缩实验 2.1.检查是否安装mod_deflate模块 2.2.重新编译安装Apache添 ...

  5. 一个极其朴素的目录扫描Python脚本

    如果目录扫描工具被某些种类的waf拦截了,可以尝试(只是尝试,不是绝对能过)用脚本发出请求,具体功能看需求而定 原理...没啥原理,也没啥技术含量,相信大家都能看得懂的,nobody knows be ...

  6. 51单片机—使用PWM对直流电机调速

    文章目录 - 什么是PWM - PWM是怎么对直流电机进行调速的 - 通过定时器中断实现PWM调速 - 上代码 - 什么是PWM PWM(脉宽调制),是靠改变脉冲宽度来控制输出电压,通过改变周期来控制 ...

  7. Longhorn,企业级云原生容器分布式存储 - 备份与恢复

    内容来源于官方 Longhorn 1.1.2 英文技术手册. 系列 Longhorn 是什么? Longhorn 企业级云原生容器分布式存储解决方案设计架构和概念 Longhorn 企业级云原生容器分 ...

  8. 黑马JVM教程——自学笔记(二)

    三.垃圾回收 3.1.如何判断对象可以回收 3.1.1 引用计数法 弊端:循环引用时,两个对象的计数都为1,导致两个对象都无法被释放 3.1.2 可达性分析算法 JVM中的垃圾回收器通过可达性分析来探 ...

  9. redisson 分布式加锁

    <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring- ...

  10. mybatis学习日志一

    Mybatis 介绍 MyBatis 是支持 普通 SQL 查询 , 存储过程 和 高级映射 的优秀持久层框架.MyBatis 消除了几乎所有的 JDBC 代码和参数的手工设置以 及对结果集的检索封装 ...