本文始发于个人公众号: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. 最小割经典题(两个点依附在一起的情况)poj3469

    Dual Core CPU Time Limit: 15000MS   Memory Limit: 131072K Total Submissions: 25099   Accepted: 10866 ...

  2. Python3高级核心技术97讲

    可以毫不夸张的说:这门课程是初中级Python开发人员向高级进阶的必学课程 许多Pythoner喜欢追求新的框架,但却不重视Python本身基础知识的学习, 他们不知道的是,语言本身的进阶优先于框架, ...

  3. 【Mac】屏蔽系统升级更新

    三行代码解决Mac升级弹窗,小红点数字1

  4. Java 8 中如何优雅的处理集合

    Java 8 中如何优雅的处理集合(Stream API) 在Java中,集合和数组是我们经常会用到的数据结构,需要经常对他们做增.删.改.查.聚合.统计.过滤等操作.相比之下,关系型数据库中也同样有 ...

  5. 当.Net成为大厂门槛代码小白该何去何从?

    掌握.Net已成为进入大厂的通行牌.越来越多的互联网软件公司开始使用.Net Core,根据去年数据显示腾讯.网易.顺丰.携程.中通.申通.同程艺龙.微医.233网校.问卷星.金蝶等关键业务已经在往. ...

  6. 1700人点反对的LeetCode问题,是因为太难了吗?

    本文始发于个人公众号:TechFlow,原创不易,求个关注 今天是LeetCode专题的第40篇文章,我们一起来看的是LeetCode中的71题Simplify Path,中文名是简化路径. 这题的难 ...

  7. [SD心灵鸡汤]010.每月一则 - 2016.02

    1.世上只有一种英雄主义,就是在认清生活真相之后依然热爱生活. 2.要想赢,就一定不能怕输.不怕输,结果未必能赢.但是怕输,结果则一定是输. 3.你要做的就是别人换不掉的,那你做不到怪谁,就是你自己没 ...

  8. [Android-NDK编译] ndk 编译 c++ 兼容性问题汇总整理

    1.__int64找不到符号 采用int64_t来代替: #if defined(__ANDROID__) typedef int64_t __int64; #endif 2.<sys/io.h ...

  9. 2018京东校招Java笔试题

    相比阿里巴巴,京东的题都是考研基础题,加上一点java基础知识和linux命令. 1. 单选题(19道题,每题2分): 1)4个并发进程都需要5个同类资源,则至少需要多少个资源,才不会导致死锁? 2) ...

  10. WebSocket是什么,有什么作用和特点?

    WebSocket是一种在单个TCP连接上进行全双工通信的协议. Websocket是基于HTTP协议的,或者说借用了HTTP的协议来完成一部分握手.具有持久化的特性 特点: 保持连接状态.与HTTP ...