Go语言 5 函数
文章由作者马志国在博客园的原创,若转载请于明显处标记出处:http://www.cnblogs.com/mazg/
今天,我们来学习Go语言编程的第五章,函数。首先简单说一下函数的概念和作用。函数是一系列语句的集合。一般是为了完成某一特定功能而定义的。这样在需要使用该功能时,直接调用该函数即可,而不用再去写一堆代码,实现了代码的复用。另外,在需要修改该功能时,也只需修改这一个函数即可,方便了代码的维护。
5.1 函数定义
函数定义包括函数名、形参列表、返回值列表以及函数体。一般语法格式如下:
func 函数名称( [形参列表] ) [返回值列表]{ 函数体 } |
示例:
func Max(a int, b int) int { if a > b { return a } return b } func main() { max := Max(10, 5)//调用函数,结果10 fmt.Println(max) max = Max(-10, 5)//调用函数,结果5 fmt.Println(max) } |
Go函数中的参数没有默认参数值。形参和返回值的变量名对于函数调用者而言没有意义。
5.2 可变参数
参数数量可变的函数称为可变参数的函数。在定义可变参数函数时,需要在参数列表的最后一个参数类型之前加上省略符号“...”,表示该函数可接收任意数量的该类型参数。
func Sum(vals ...int) int { total := 0 for _, v := range vals { total += v } return total } |
func main() { fmt.Println(Sum(1, 2)) //2个参数 fmt.Println(Sum(1, 2, 3)) //3个参数 slice := []int{1, 2, 3} //把slice作为参数,需要打散 fmt.Println(Sum(slice...)) } |
5.3 多返回值
与C、C++和Java等其它开发语言不同的是是Go语言的函数可以有多个返回值。这个特性能够使得我们写出比其它语言更加优雅和简洁的代码,例如File.Read()函数可以同时返回读取的字节数和错误信息。如果读取成功,返回值n为读取的字节数,err为nil,否则err为具体的错误信息。
func (file *File) Read(b []Byte)(n int,err Error) |
一个简单的多返回值示例:
// 多个返回值,另外同类型的参数列表或返回值列表可以采用以下简写形式 func AddAndSub(a, b int) (add, sub int) { return a + b, a - b } func main() { v1, v2 := AddAndSub(6, 3) fmt.Println(v1, v2) // 9,3 } |
5.4 递归函数
程序调用自身的编程技巧称为递归。递归做为一种算法在编程语言中广泛应用。函数调用自身,称为递归函数。
指的是这样一个数列:0、1、1、2、3、5、8、13、21、……在数学上,斐波纳契数列以如下被以递归的方法定义:F0=0,F1=1,Fn=F(n-1)+F(n-2)(n>=2,n∈N*),用文字来说:
² 前2个数是 0 和 1 。
² 第 i 个数是第 i-1 个数和第i-2 个数的和。
// 递归实现, func Fib(n int) int { if n < 2 { return n } return Fib(n-1) + Fib(n-2) } func main() { for i := 0; i < 10; i++ { fmt.Print(Fib(i), " ") } } |
5.5 错误处理
对于大多数函数而言,无法确保函数一定能够成功运行,因为产生错误的原因很有可能超出程序员控制范围。例如,任何I/O操作的函数都有出现错误的可能。如果函数需要返回错误,通常将error作为多种返回值中的最后一个,虽然这并非是强制要求。
一般为如下模式:
func Foo(参数列表)(ret list, err error) { |
调用时的处理模式:
n, err := Foo(参数) |
内置的error是接口类型。
常用的处理错误的5种方式:
1传递错误
2 如果错误是偶然的,重新尝试失败的操作。
3 输出错误信息,并结束程序,一般在main()函数中使用。
4 输出错误信息,并且不中断程序
5 直接忽略错误
5.6 defer
当一个函数调用前有关键字 defer 时, 那么这个函数的执行会推迟到包含这个defer 语句的函数即将返回前才执行。
func main() { defer fmt.Println(3) fmt.Println(1) fmt.Println(2) } //打印结果: 1 2 3 |
defer通常用于 open/close, connect/disconnect, lock/unlock 等这些成对的操作, 来保证在任何情况下资源都被正确释放。下面是一个文件拷贝的例子:
func CopyFile(dst, src string) (w int64, err error) { srcFile, err := os.Open(src) if err != nil { return } defer srcFile.Close() dstFile, err := os.Create(dstName) if err != nil { return } defer dstFile.Close() return io.Copy(dstFile, srcFile) } |
即使其中的Copy()函数抛出异常, Go仍然会保证dstFile和srcFile会被正常关闭。一个函数中可以存在多个defer语句, defer语句的调用是遵照先进后出的原则,即最后一个defer语句将最先被执行。
defer也可以跟一个用户自定义的匿名函数。
defer func() { // 匿名函数的代码 } () |
defer 调用的函数参数的值在defer被定义时就确定了,而 defer 函数内部所使用的变量的值是在这个函数运行时才确定。
func main() { i := 1 defer fmt.Println("延时打印:", i) //i是参数 i++ fmt.Println("常规打印:", i) } //打印结果: 常规打印: 2 延时打印: 1 func main() { i := 1 defer func() { fmt.Println("延时打印", i) //i是内部变量 }() i++ fmt.Println("常规打印:", i) } //打印结果: 常规打印: 2 延时打印 2 |
defer 函数调用的执行时机是外层函数设置返回值之后, 并且在即将返回之前。也就是说“return 返回值”语句并不是原子的。请看下面的例子:
func double(x int) int { return x + x } func triple(x int) (r int) { defer func() { r += x }() return double(x) } func main() { fmt.Println(triple(3)) } 打印结果: 9 // 等价与下面代码,打印结果是9 func triple(x int) (r int) { // 1 设置返回值 r = double(x) // 2 执行defer语句的函数 func() { r += x }() // 3 函数返回 return } |
5.7 panic、recover函数
Go语言追求简洁优雅,所以不支持传统的 try…catch…finally 这种异常处理结构。Go语言的设计者们认为,将异常与控制结构混在一起会很容易使得代码变得混乱,不要用异常代替错误,更不要用来控制流程。在遇到真正的异常的情况下,才使用Go中引入的Exception处理:defer, panic, recover。panic 是用来表示非常严重的不可恢复的错误的,一般会导致程序挂掉(除非recover)。Go语言对异常的处理可以这样简单描述:Go程序的执行过程中可以抛出一个panic的异常,然后在defer中通过recover捕获这个异常。
func main() { defer func() { // 必须要先声明defer,否则不能捕获到panic异常 fmt.Println("c") if err := recover(); err != nil { fmt.Println(err) // 这里的err其实就是panic传入的内容 } fmt.Println("d") }() foo() } func foo() { fmt.Println("a") panic("产生异常") fmt.Println("b") fmt.Println("f") } //打印结果: a c 产生异常 d |
5.8 函数值
在Go中,函数被看作第一类值。函数值像其它值一样,拥有类型。函数类型可以使用type关键字定义,通过函数的参数和返回值区分。
函数值可以被赋值给其它变量,也可以作为其他函数的参数或返回值。对函数值的调用相当于对函数的调用。函数值属于引用类型,两个函数值之间不可比较。
package main import "fmt" type handle func(int) int func main() { //1给函数变量赋值 v := func(n int) int { return n * n } //2 通过函数值调用函数 fmt.Println(v(3)) var h handle h = v fmt.Println(h(3)) } |
5.9 匿名函数与闭包
1 匿名函数
顾名思义,匿名函数就是定义时没有函数名称的函数。
func main() { add := func(x, y int) int { //匿名函数赋值给变量add return x + y } fmt.Println(add(10, 5)) //通过变量调用函数 func(x, y int) { fmt.Println(x - y) }(10, 5) //定义时直接调用 } |
2 闭包
闭包是可以包含自由(未绑定到特定对象)变量的代码块,这些变量不在这个代码块内或者任何全局上下文中定义,而是在定义代码块的环境中定义。要执行的代码块为自由变量提供绑定的计算环境(由于自由变量包含在代码块中,所以这些自由变量以及它们引用的对象没有被释放)。
闭包是由函数及其相关引用环境组合而成的实体(即:闭包=函数+引用环境)。
package main import "fmt" func Sqrt(num int) func() int { //变量num与匿名函数在同一环境中定义 return func() int { num++ return num * num } } func main() { s := Sqrt(0) //外部函数执行完成后,num并没有被销毁,值为0; fmt.Println(s()) //执行s函数,num的值为1,函数的返回值为1 fmt.Println(s())//执行s函数,num的值为2,函数的返回值为4 fmt.Println(s())//执行s函数,num的值为3,函数的返回值为9 } //打印结果:1 4 9 |
闭包函数出现的条件:
1.被嵌套的函数引用到非本函数的外部变量,而且这外部变量不是“全局变量”
2.嵌套的函数被独立了出来(被父函数返回或赋值 变成了独立的个体),而被引用的变量所在的父函数已结束。
逃逸的函数内的局部变量一定是堆上分配存储空间的。
Go语言 5 函数的更多相关文章
- C语言pow函数编写
C语言pow函数编写 #include<stdio.h> double chaoba(double f,double q); //声明自定义函数 void main(void) { dou ...
- C语言-自定义函数
C语言自定义函数 --1-- 自定义函数定义 1.1 无参无返回值函数 1.2 无参有返回值函数 1.3 有参无返回值函数 1.4 有参有返回值函数 --2-- 函数的参数 2.1 形式参数介绍和使用 ...
- C语言printf()函数:格式化输出函数
C语言printf()函数:格式化输出函数 头文件:#include <stdio.h> printf()函数是最常用的格式化输出函数,其原型为: int printf( char ...
- C语言的函数
"函数"在英文的翻译是"function",无论在自然科学还是计算机科学都是这个词,而"function"的本意是"功能" ...
- c语言main函数返回值、参数详解(返回值是必须的,0表示正常退出)
C语言Main函数返回值 main函数的返回值,用于说明程序的退出状态.如果返回0,则代表程序正常退出:返回其它数字的含义则由系统决定.通常,返回非零代表程序异常退出. 很多人甚至市面上的一些书籍,都 ...
- Go语言示例-函数返回多个值
Go语言中函数可以返回多个值,这和其它编程语言有很大的不同.对于有其它语言编程经验的人来说,最大的障碍不是学习这个特性,而是很难想到去使用这个特性. 简单如交换两个数值的例子: package mai ...
- 【学习笔记】【C语言】函数
一. 什么是函数 任何一个C语言程序都是由一个或者多个程序段(小程序)构成的,每个程序段都有自己的功能,我们一般称这些程序段为“函数”.所以,你可以说C语言程序是由函数构成的. 比如你用C语言编写了一 ...
- 【转载】 c语言inline函数的使用
c语言inline函数的使用 转载自:http://blog.chinaunix.net/uid-21843265-id-3056446.html 大学在教科书上学习过inline函数,定义为inli ...
- 【C语言】函数和自定义函数
函数,我之前也提到过一点点内容.其实函数是很好理解的,但是写起来又十分麻烦. 一. 函数引入 我们知道,C源程序是由函数组成的.请看下面的简单函数例子 #include <stdio.h ...
- c语言中函数的简单介绍
c语言中函数的介绍: 函数,简单的说就是代码的打包.存放在一个地方,当需要的时候调用. 函数分类: 1.无参无返回值函数 void func() 2.无参有返回值函数 int func() 3.有参 ...
随机推荐
- c 读取文本
#include <stdio.h> #include <stdlib.h> #include <string.h> #define max 10 #define ...
- jquery弹出层开源框架layer
高度自适应参考:layer.open如何让高度自适应? 高度自适应修改layer.js代码如下: r.iframeAuto = function(e) { if (e) { var t = r.get ...
- 第132天:移动web端-rem布局(进阶)
rem布局(进阶版) 该方案使用相当简单,把下面这段已压缩过的 原生JS(仅1kb,源码已在文章底部更新,2017/5/3) 放到 HTML 的 head 标签中即可(注:不要手动设置viewport ...
- 【ABP】Abp的AspNetZero5.0版本无法使用ctrl+f5调式
原文:http://www.cnblogs.com/94pm/p/7942483.html AspNetZero是基于Abp框架开发的商业程序,最近从Abp交流群中得知5.0版本开始加入了防盗版的功能 ...
- HDU4646_Laser Beam
题目是这样的,一个等边三角形,三边都是有镜子组成的. 现在要你从一个点射入一条光线,问你如果要求光线在三角形里面反射n次然后从入点射出来的话,入射的方向可能有多少种? 这.....其实不难.关键是要搞 ...
- BZOJ 1853 幸运数字(容斥原理+dfs)
题意:求闭区间内能被6和8组成的数字整除的数目.n<=1e11. 我们可以预处理出这些6和8组成的数字,大概2500个,然后排除一些如88,66的情况.这样大概还剩下1000个. 转化为[0,r ...
- bzoj1272 Gate Of Babylon(计数方法+Lucas定理+乘法逆元)
Description Input Output Sample Input 2 1 10 13 3 Sample Output 12 Source 看到t很小,想到用容斥原理,推一下发现n种数中选m个 ...
- 【SPOJ】Count On A Tree II(树上莫队)
[SPOJ]Count On A Tree II(树上莫队) 题面 洛谷 Vjudge 洛谷上有翻译啦 题解 如果不在树上就是一个很裸很裸的莫队 现在在树上,就是一个很裸很裸的树上莫队啦. #incl ...
- 使图片相对于上层DIV始终水平、垂直都居中
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- hive1.1.0建立外部表关联HDFS文件
0. 说明 已经安装好Hadoop和hive环境,hive把元数据存储在mysql数据库.这里仅讨论外部表和HDFS的关联,并且删掉外部表之后,对HDFS上的文件没有影响. 1. 在HDFS创建分区, ...