Golang简单入门教程——函数进阶篇
本文始发于个人公众号: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简单入门教程——函数进阶篇的更多相关文章
- 携程Apollo简单入门教程这一篇就够了
1. Apollo背景 对程序配置的期望值也越来越高:配置修改后实时生效,灰度发布,分环境.分集群管理配置,完善的权限.审核机制…… 废话不多说,参考官方文档 如果不想看文档, 也没关系, 跟 ...
- Linux Capabilities 入门教程:进阶实战篇
原文链接:https://fuckcloudnative.io/posts/linux-capabilities-in-practice-2/ 该系列文章总共分为三篇: Linux Capabilit ...
- NumPy简单入门教程
# NumPy简单入门教程 NumPy是Python中的一个运算速度非常快的一个数学库,它非常重视数组.它允许你在Python中进行向量和矩阵计算,并且由于许多底层函数实际上是用C编写的,因此你可以体 ...
- Systemd 入门教程:实战篇
Systemd 入门教程:实战篇 上一篇文章,介绍了 Systemd 的主要命令,这篇文章主要介绍如何使用 Systemd 来管理我们的服务,以及各项的含义: 一.开机启动 对于那些支持 System ...
- 【转帖】Systemd 入门教程:命令篇
Systemd 入门教程:命令篇 Copy From http://www.ruanyifeng.com/blog/2016/03/systemd-tutorial-commands.html 感觉 ...
- Linux Capabilities 入门教程:概念篇
原文链接:Linux Capabilities 入门教程:概念篇 Linux 是一种安全的操作系统,它把所有的系统权限都赋予了一个单一的 root 用户,只给普通用户保留有限的权限.root 用户拥有 ...
- 程序员,一起玩转GitHub版本控制,超简单入门教程 干货2
本GitHub教程旨在能够帮助大家快速入门学习使用GitHub,进行版本控制.帮助大家摆脱命令行工具,简单快速的使用GitHub. 做全栈攻城狮-写代码也要读书,爱全栈,更爱生活. 更多原创教程请关注 ...
- GitHub这么火,程序员你不学学吗? 超简单入门教程 【转载】
本GitHub教程旨在能够帮助大家快速入门学习使用GitHub. 本文章由做全栈攻城狮-写代码也要读书,爱全栈,更爱生活.原创.如有转载,请注明出处. GitHub是什么? GitHub首先是个分布式 ...
- Linux 命令详解(八)Systemd 入门教程:实战篇
Systemd 入门教程:实战篇 http://www.ruanyifeng.com/blog/2016/03/systemd-tutorial-part-two.html
随机推荐
- IDEA启动springboot项目找不到application.yml配置文件
idea启动项目时读取不到application-pro.yml文件,但是配置文件都在resource目录下: 解决:target/classes 目录是IDEA的classpath目录,项目编译后配 ...
- SimpleAuthenticationInfo的参数
SimpleAuthenticationInfo的参数 仅供个人参考,以及学习记录.SimpleAuthenticationInfo authenticationInfo = new SimpleAu ...
- Mysql-NULL转数字
最近做了一个学生成绩表,其中遇到一个小问题 需要统计个门科目的平均成绩,在统计到高等数学时,因为高数没有人考,在成绩表中根本不存在的分数,但是在课程表存在高数科目. 当这两个表内联然后统计分数,这样会 ...
- PHP数组简单操作
1.数字索引数组 1.1创建一个数组 php中最常用的两个类型是:数组,字符串.数组也分为两种,一种是数字索引,一种是关键是索引,关键字索引有点像python中字典的意思.数字索引类型的数组的创建方法 ...
- DBCP连接池和事物
工具类案例 public static final String DRIVER = "com.mysql.jdbc.Driver"; public static final Str ...
- 理解session及微信小程序使用session
session介绍 由于Http是无状态的协议,所以服务端需要记录用户的状态时,就需要某种机制来识别具体的用户,实现这个机制的方式就是session. 典型的场景比如购物车,当你点击下单按钮时,由于H ...
- vue中 vue-awesome-swiper的使用
在页面开发中,经常会碰到需要轮播,滑动等需求,特别是多元素滑动,此时,要么自己写,要么网上找轮子,不过自己写,其实还是有点难度的,一般就是网上找写好的库,本文就是针对vue-awesome-swipe ...
- group by和having注意事项
执行和编写顺序:join->where->group by->having 其中where与having的区别: where用于在group by分组之前过滤掉某些行, group ...
- AtomicLong AtomicDouble AtomicInteger
Atomic+数字类型 大多都持有一个静态的Unsafe对象,通过unsafe 对属性在类对象的offset cas直接操作物理内存实现对数据的修改 public class AtomicLong e ...
- 跨域解决方案 - webpack devServer
1. 定义 如果一个项目中配置了webpack, 那么我们使用 webpack devServer 来配置代理转发请求来达到解决跨域问题的目的 webpack devServer 能够解决跨域问题的根 ...