本文始发于个人公众号:TechFlow,原创不易,求个关注

今天是golang专题的第八篇,我们来聊聊golang当中的函数。

我们在之前的时候已经介绍过了函数的基本用法,知道了怎么样设计或者是定义一个函数,以及怎么样调用一个函数,还了解了defer的用法。今天这篇文章我们来继续深入这个话题,来看看golang当中关于函数的一些进阶的用法。

返回error

前文当中我们曾经提到过,在golang当中并没有try catch捕获异常的机制。在其他语言当中异常只有一种,可以通过try catch语句进行捕获,而golang当中做了区分,将异常分为两种,一种是可以在函数当中返回的error,另外一种是严重的会引起程序崩溃的panic

在golang中,error也是一个数据类型,由于golang支持函数的多值返回,所以我们可以设置一个返回值是error。我们通过对这个error的判断来获取运行函数的情况。

举个例子,比如说,假设我们实现一个Divide函数实现两个int相除。那么显然我们需要除数不能为0,当除数为0的时候我们需要返回一个异常。这个时候我们可以把代码写成这样:

// Divide test
func Divide(a, b int) (ret int, err error) {if b == 0 {err = errors.New("divisor is zero")return}return a / b, nil
}

当我们调用函数的时候,我们用两个变量去接收这个函数返回的结果,第二个变量的类型是error。当这个函数成功执行的时候第二个变量的结果为nil,我们只需要判断它是否等于nil,就可以知道函数执行是否成功。如果不成功,我们还可以记录失败的原因。

func main() {ret, err := Divide(5, 2)if err == nil {fmt.Println(ret)} else {fmt.Println(err)}
}

这种用法在golang当中非常常见,我们之前在介绍字符串相关操作的时候也介绍过返回error的用法。我们在设计函数的时候如果需要判断输入的合法性可以使用error,这样就可以保证handle住非法的情况,并且也能让下游感知到。

不定参数

不定参数的用法在很多语言当中都有,比如在Python当中,不定参数是*args。通过*args我们可以接受任何数量的参数,由于Python是弱变量类型的语言,所以args这些参数的类型可以互不相同。但是golang不行,golang严格限制类型,不定参数必须要保证类型一样。除此之外,其他的用法和Python一样,不定参数会以数组的形式传入函数内部,我们可以使用数组的api进行访问。

我们来看一个例子,我们通过...来定义不定参数。比如我们可以实现一个sum函数,可以将任意个int进行累加。

func Sum(nums ... int) int{ret := 0for _, num := range nums {ret += num}return ret
}

我们来仔细研究一下上面这个例子,在这个例子当中,我们通过...传入了一个不定参数,我们不定参数的类型只写一次,写在...的后面。从底层实现的机制上来说,不定参数本质上是将传入的参数转化成数组的切片。但是这就有了一个问题,既然传入的是一个数组的切片,我们为什么要专门设置一个关键字,而不是规定传入一个切片呢?

比如上面的代码我们完全可以写成这样:

func Sum(nums []int) int{ret := 0for _, num := range nums {ret += num}return ret
}

无论从代码的阅读还是编写上来看相差并不大,好像这样做完全没有意义,其实不是这样的。这个关键字简化的并不是函数的设计方,而是函数的使用方。如果我们规定了函数的输入是一个切片,那么当我们在传入数据的时候,必须要使用强制转化,将我们的数据转化成切片,比如这样:

Sum([]int(3, 4, 6, 8))

而使用...关键字我们则可以省略掉强制转化的过程,上面的代码我们写成这样就可以了:

Sum(3, 4, 6, 8)

很明显可以看出差异,使用不定参数的话调用方会轻松很多,不需要再进行额外的转换。如果我们要传入的也是一个数组,那么在传递的时候也需要用...符号将它展开

a := make([]int)
a = append(a, 3)
a = append(a, 4)
Sum(a...)
Sum(a[1:]...)

既然聊到不定参数的传递,那么又涉及到了一个问题,当我们想要像Python那样传递多个类型不同的参数的时候,应该怎么办呢?按照道理golang是静态类型的语言,限制死了参数的类型,是不能随便转换的才对。但是偏偏这样操作是可以的,因为golang当中有一个特殊的类型,叫做interface

interface的用法很多,一个很重要的用法是用在面向对象当中充当结构体的接口。这里我们不做过多深入,我们只需要知道,interface的一个用法是可以用来代替所有类型的变量。我们来看一个例子:

func testInterface(args ...interface{}) {for _, arg := range args {switch arg.(type) {case int:fmt.Println("it's a int")case string:fmt.Println("it's a string")    case float32:fmt.Println("it's a float")default:fmt.Println("it's an unknown type")}}
}func main() {testInterface(3, 4.5, "abc")
}

我们可以用.(type)获取一个interface变量实际的类型,这样我们就实现了任意类型任意数量参数的传入。

匿名函数和闭包

匿名函数我们在Python当中经常使用到,其实这个概念出现已久,最早可以追溯到1958年Lisp语言。所以这并不是一个新鲜的概念,只是传统的C、C++等语言没有支持匿名函数的功能,所以显得好像是一个新出现的概念一样。golang当中也支持匿名函数,但是golang当中匿名函数的使用方式和Python等语言稍稍有些不同。

在Python当中我们是通过lambda关键字来定义匿名函数,它可以被传入另一个函数当中,也可以赋值给一个变量。golang当中匿名函数的定义方式和普通函数基本是一样的,只是没有函数名而已,不过它也可以被传入函数或者是赋值给另一个变量。

比如:

s := func(a, b int) int {return a + b
}c := s(3, 4)

除了匿名函数之外,golang还支持闭包。闭包的概念我们在之前Python闭包的介绍当中曾经提到过,我们之前也用过好几次,闭包的本质不是一个包,而是一个函数,是一个持有外部环境变量的函数。比如在Python当中,我们经常可以看到这样的写法:

def outside(x):def inside(y):print(x, y)return insideins = outside(3)
ins(5) #3, 5

我们可以看到outside这个函数返回了inside这个函数,对于inside这个函数而言,它持有了x这个变量。x这个变量并不是属于它的,而是定义在它的外部域的。并且我们在调用inside的时候是无法干涉这个变量的,这就是一个闭包的典型例子。根据轮子哥的说法,闭包的闭的意思并不是封闭内部,而是封闭外部。当外部scope失效的时候,函数仍然持有一份外部的环境的值。

golang当中闭包的使用方法大同小异,我们来看一个类似的例子:

func main() {a := func(x int) (func(int)) {return func(y int){fmt.Println(x, y)}}b := a(4)b(5)
}

这个闭包的例子和刚才上面Python那个例子是一样的,唯一不同的是由于golang是强类型的语言,所以我们需要在定义闭包的时候将输入和输出的类型定义清楚。

总结

关于golang当中函数的高级用法就差不多介绍完了,这些都是实际编程当中经常使用的方法,如果想要学好golang这门语言的话,这些是基本功。如果你之前有其他语言的基础,来写go的话,整体上手的难度还是不大的,很多设计都可以在其他的语言当中找到影子,有了参照来学会简单得多。

我很难描述实际工作当中写golang的体验,和我写任何一门其他的语言都不一样,有一种一开始期望很低,慢慢慢慢总能发现惊喜的感觉。我强烈建议大家去实际感受一下。

如果喜欢本文,可以的话,请点个关注,给我一点鼓励,也方便获取更多文章。

golang 定义一个空切片_Golang简单入门教程——函数进阶使用相关推荐

  1. golang 定义一个空切片_Golang slice切片操作之切片的追加、删除、插入等

    本文介绍了Golang slice切片操作之切片的追加.删除.插入等,分享给大家,具体如下: 一.一般操作 1,声明变量,go自动初始化为nil,长度:0,地址:0,nil func main(){ ...

  2. golang 定义一个空切片_Golang切片 一个隐讳的坑

    今天刷题leetcode.78子集,其中遇到一个语法坑,查了半小时最后才定位是切片的使用问题,现在来总结一下,直接上代码. func Test_Slice(t *testing.T) {s1 := [ ...

  3. golang 切片 接口_Golang简单入门教程——函数进阶使用

    本文始发于个人公众号:TechFlow,原创不易,求个关注 今天是golang专题的第八篇,我们来聊聊golang当中的函数. 我们在之前的时候已经介绍过了函数的基本用法,知道了怎么样设计或者是定义一 ...

  4. 定义一个空切片_Python进阶:全面解读高级特性之切片

    优质文章,第一时间送达! 众所周知,我们可以通过索引值(或称下标)来查找序列类型(如字符串.列表.元组-)中的单个元素,那么,如果要获取一个索引区间的元素该怎么办呢? 切片(slice)就是一种截取索 ...

  5. 结构体后面定义一个空数组的含义

    最近在写C代码,经常看到Linux 的头文件中有的结构体后面会定义一个空数组,不知道其为何作用?经过高人指点终于明白其要点! struct inotify_event {    __s32 wd;   ...

  6. php 怎么定义一个空对象,php定义空对象的方法

    本文主要和大家分享php定义空对象的方法,有时候我们直接对不存在的数组直接定义其下标的值,不会报错,但是我们定义不存在的对象的时候,就会报错,这个时候我们定义一个空对象即可.有以下三种方法:<? ...

  7. php 返回一个空对象,PHP如何定义一个空对象

    使用新数组我这样做: $aVal = array(); $aVal[key1][var1] = "something"; $aVal[key1][var2] = "som ...

  8. uni-ui简单入门教程 - 如何用HBuilderX为uni-app项目启用uni-ui扩展组件?

    须知 uni-app是一个前端框架 简单来说,uni-app的组件,类似HTML的标签,例如a转navigation.span转text等 uni-app的组件包括 基础组件 (自带免安装) + 扩展 ...

  9. emacs 自带的简单入门教程

    emacs 自带的教程是 英文版和繁体中文版,下面的内容是利用在线繁体转简单工具生成 有些地方翻译的不精准,凑和看 在emacs 中按下 Ctrl-h t 或者F1 t即可打开自带的此文档 原文:em ...

最新文章

  1. Marcin Grzejszczak访谈:Spring Cloud Contract
  2. 一份超详细的 Java 问题排查工具单
  3. 如何打造具有绝对市场竞争力的团队
  4. 音频开发中常见的四个错误
  5. 牛客网【每日一题】 合集
  6. 数据结构之插入排序:折半插入排序算法
  7. 基于图结构的图合成与差分隐私【LDPGen】
  8. readline库实现命令行自动补全
  9. java 通用类型_Java获取通用类型的集合
  10. html怎么键tab键,tab键的html
  11. python删除图片文字_ps去掉图片上的文字的6种方法
  12. 国密SM2非对称算法与实现
  13. 车载诊断系统-OBD
  14. 基于深度学习的菠萝实时三维坐标定位项目
  15. 炒币机器人:币圈炒币是怎么亏钱的
  16. Anthony_tester(博客链接)
  17. GNSS原理与应用(五)——GPS卫星信号
  18. Windows定时开启或关闭声音音量
  19. 上帝掷骰子:APP Store是赌场不是金矿
  20. windows下局域网内通过NTP同步时间

热门文章

  1. 快速定位java系统的线上问题--转
  2. 利用CORS实现跨域请求--转
  3. Java + MongoDB Hello World Example--转载
  4. Javascript操纵Cookie--转
  5. 手动修改user-agent
  6. spring启动过程之源码跟踪(下)--spring Debug
  7. Lesson 7(12)神经网络的诞生与发展机器学习基本概念
  8. 万物上链: 5G 起跑
  9. 迪拜与IBM合作推出基于区块链的商业注册系统
  10. 极大似然估计的朴素理解