本文始发于个人公众号: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 := 0
for _, num := range nums {
ret += num
}
return ret
}

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

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

func Sum(nums []int) int{
ret := 0
for _, 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 inside ins = 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的体验,和我写任何一门其他的语言都不一样,有一种一开始期望很低,慢慢慢慢总能发现惊喜的感觉。我强烈建议大家去实际感受一下。

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

本文使用 mdnice 排版

Golang简单入门教程——函数进阶篇的更多相关文章

  1. 携程Apollo简单入门教程这一篇就够了

    1. Apollo背景 对程序配置的期望值也越来越高:配置修改后实时生效,灰度发布,分环境.分集群管理配置,完善的权限.审核机制……   废话不多说,参考官方文档   如果不想看文档, 也没关系, 跟 ...

  2. Linux Capabilities 入门教程:进阶实战篇

    原文链接:https://fuckcloudnative.io/posts/linux-capabilities-in-practice-2/ 该系列文章总共分为三篇: Linux Capabilit ...

  3. NumPy简单入门教程

    # NumPy简单入门教程 NumPy是Python中的一个运算速度非常快的一个数学库,它非常重视数组.它允许你在Python中进行向量和矩阵计算,并且由于许多底层函数实际上是用C编写的,因此你可以体 ...

  4. Systemd 入门教程:实战篇

    Systemd 入门教程:实战篇 上一篇文章,介绍了 Systemd 的主要命令,这篇文章主要介绍如何使用 Systemd 来管理我们的服务,以及各项的含义: 一.开机启动 对于那些支持 System ...

  5. 【转帖】Systemd 入门教程:命令篇

    Systemd 入门教程:命令篇  Copy From http://www.ruanyifeng.com/blog/2016/03/systemd-tutorial-commands.html 感觉 ...

  6. Linux Capabilities 入门教程:概念篇

    原文链接:Linux Capabilities 入门教程:概念篇 Linux 是一种安全的操作系统,它把所有的系统权限都赋予了一个单一的 root 用户,只给普通用户保留有限的权限.root 用户拥有 ...

  7. 程序员,一起玩转GitHub版本控制,超简单入门教程 干货2

    本GitHub教程旨在能够帮助大家快速入门学习使用GitHub,进行版本控制.帮助大家摆脱命令行工具,简单快速的使用GitHub. 做全栈攻城狮-写代码也要读书,爱全栈,更爱生活. 更多原创教程请关注 ...

  8. GitHub这么火,程序员你不学学吗? 超简单入门教程 【转载】

    本GitHub教程旨在能够帮助大家快速入门学习使用GitHub. 本文章由做全栈攻城狮-写代码也要读书,爱全栈,更爱生活.原创.如有转载,请注明出处. GitHub是什么? GitHub首先是个分布式 ...

  9. Linux 命令详解(八)Systemd 入门教程:实战篇

    Systemd 入门教程:实战篇 http://www.ruanyifeng.com/blog/2016/03/systemd-tutorial-part-two.html

随机推荐

  1. dubbo分布式应用

    dubbo介绍: dubbo是一款分布式服务框架,支持高性能和透明化的RPC远程服务调用方案,每天为2千多个服务提供大于30亿次访问量支持,并被广泛应用于阿里巴巴集团的各成员站点以及别的公司的业务中. ...

  2. mysql创建本地用户及赋予数据库权限的方法示例

    大家在安装 mysql 时通常会生成一个超级用户 root,很多人之后就一直沿用这一个用户,虽然这会很方便,但超级用户权限太大,在所有地方使用它通常是一个安全隐患. 这一点跟操作系统的用户管理也是类似 ...

  3. thymeleaf抛出项目上下文ServletContext ,session,request等信息

    @RequestMapping("/alls") public String allsinfo(HttpSession session, HttpServletRequest re ...

  4. 【java】关键字volatile

    volatile 1. 含义: volatile是JVM提供的轻量级的同步机制,具有三个特点:保证可见性.不保证原子性.禁止指令重排. 1.1 保证可见性 一个线程修改了共享变量并写回主内存,其他线程 ...

  5. Android_适配器(adapter)之ArrayAdapter

    ArrayAdapter是一个很简单的适配器,是BaseAdapter的子类. ArrayAdapter绑定的数据是集合或数组,比较单一.视图是列表形式,ListView 或 Spinner. Arr ...

  6. Python学习之路【第一篇】:Python简介与入门

    Python简介 一.什么是Python Python 是一个高层次的结合了解释性.编译性.互动性和面向对象的脚本语言. Python 的设计具有很强的可读性,相比其他语言经常使用英文关键字,其他语言 ...

  7. 【书签】连续型特征的归一化和离散特征的one-hot编码

    1. 连续型特征的常用的归一化方法.离散型特征one-hot编码的意义 2. 度量特征之间的相关性:余弦相似度和皮尔逊相关系数

  8. 如何短时间内快速通过Java面试

    当然是刷题啊 1-10期[10期]Redis 面试常见问答[09期]说说hashCode() 和 equals() 之间的关系?[08期]说说Object类下面有几种方法呢?[07期]Redis中是如 ...

  9. web-信息泄露基础知识总结(持续更新)

    web-信息泄露 1.git泄露 Git是一个开源的分布式版本控制系统,在执行git init初始化目录的时候,会在当前目录下自动创建一个.git目录,用来记录代码的变更记录等.发布代码的时候,如果没 ...

  10. Rocket - tilelink - SRAM

    https://mp.weixin.qq.com/s/-z9n6SHyAiK2OE7mOSvC2Q   简单介绍SRAM的实现.   ​​   1. 基本介绍   实现一个支持读写的静态存储器.存取的 ...